# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
This site has an agentic ask interface you may use to query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.kinesis.network/index.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

---

# Compute, on your terms.

Deploy containers. Choose your compute. Build with agents.

Bring a container or a GitHub repository. Run it on serverless CPU capacity, dedicated cloud servers, or infrastructure you already own. Kinesis brings deployment, operations and cost visibility into one place. GPU workloads run on grids backed by dedicated, marketplace or connected machines.

## Start building

Work in your own organization or join your team by invitation. Your role sets what you can build, operate and manage.

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><i class="fa-rocket" style="color:$primary;">:rocket:</i></td><td><h3>Deploy your first app</h3></td><td>Go from a project to a running service with the App Gallery or your own image.</td><td><a href="/getting-started/running-llms-with-kinesis-network">Deploy an app</a></td></tr><tr><td><i class="fa-code" style="color:$primary;">:code:</i></td><td><h3>Build with the API</h3></td><td>Automate deployments, inspect infrastructure and track spend with REST.</td><td><a href="/api">REST API</a></td></tr><tr><td><i class="fa-robot" style="color:$primary;">:robot:</i></td><td><h3>Connect your agent</h3></td><td>Give an MCP client the tools to deploy, diagnose and operate Kinesis.</td><td><a href="/mcp">MCP &amp; agents</a></td></tr><tr><td><i class="fa-microchip" style="color:$primary;">:microchip:</i></td><td><h3>Choose your compute</h3></td><td>Understand serverless capacity, dedicated servers and grids.</td><td><a href="/getting-started/running-llms-with-kinesis-network/understanding-compute-sources">Compute options</a></td></tr><tr><td><i class="fa-chart-line" style="color:$primary;">:chart-line:</i></td><td><h3>Operate with visibility</h3></td><td>Read logs, follow app status, inspect metrics and understand failures.</td><td><a href="/getting-started/running-llms-with-kinesis-network/monitoring-apps">Monitor and troubleshoot</a></td></tr><tr><td><i class="fa-network-wired" style="color:$primary;">:network-wired:</i></td><td><h3>Contribute compute</h3></td><td>Learn how approved providers bring idle hardware to the network.</td><td><a href="/getting-started/kinetization-of-idle-compute">Contribute compute</a></td></tr></tbody></table>

## One platform. Three ways to work.

| Interface    | Use it for                                                           | Start here                                        |
| ------------ | -------------------------------------------------------------------- | ------------------------------------------------- |
| **Portal**   | Interactive setup, configuration, logs and infrastructure management | [Open the portal](https://portal.kinesis.network) |
| **REST API** | CI pipelines, scripts and application integrations                   | [API documentation](/api)                         |
| **MCP**      | Agent-driven deployment, investigation and operations                | [Connect an agent](/mcp)                          |

Organizations bring people, resources and billing together. Projects organize apps; serverless capacity or grids determine where they run.

[Set up your organization](/organizations) · [Manage members and roles](/organizations/members)

[Explore the platform](/what-you-can-do-with-kinesis) · [Get support](/support)


# Platform overview

The resources, interfaces and compute choices that make up Kinesis.

Kinesis runs containerized applications across GPU and CPU infrastructure. Bring an image, choose an App Gallery template or connect a GitHub repository. Configure the runtime and choose where it should run.

Your work belongs to an [organization](/organizations), which owns the resources and pays for usage. Switch organizations from the account menu. Admins manage the organization, infrastructure and billing; Developers build and run accessible apps; Viewers read.

Serverless capacity currently runs CPU workloads. For GPU apps, choose an accessible grid. Organizations can also use [single sign-on](/organizations/single-sign-on) through their identity provider.

## The core resources

| Resource         | Purpose                                                       | Example                                            |
| ---------------- | ------------------------------------------------------------- | -------------------------------------------------- |
| **Organization** | Owns resources, brings members together and pays for usage    | Your team or company                               |
| **Project**      | Organizes related applications and shared access              | A product environment or research project          |
| **App**          | Defines the image, runtime, network and resource requirements | An inference endpoint, web service or batch worker |
| **Grid**         | Groups compute resources that apps may use                    | A team's dedicated GPU capacity                    |
| **Server**       | An individual machine, with its own lifecycle                 | A cloud instance or a machine you connect          |
| **Build**        | Turns a GitHub source into a deployable image                 | A Dockerfile build followed by app deployment      |

```mermaid
flowchart TD
  O[Organization] --> P[Project]
  O --> G[One or more grids]
  O --> B[Members and billing]
  P --> A[App]
  A --> S[Serverless CPU capacity]
  A --> G[One or more grids]
  G --> C[Dedicated cloud servers]
  G --> I[Your own infrastructure]
```

## Match the platform to your workload

**Model serving.** Run an inference server in a container, select GPU requirements and expose a service port. Templates provide a starting configuration; model size, memory use and concurrency determine the resources you need.

**Application services.** Deploy APIs, web applications and supporting services from an image or a repository. Use environment variables, secrets, health checks and custom domains to configure the service.

**Batch work.** Use the execution and restart-policy settings for task-oriented workloads. Inspect logs and completion state separately from the readiness of a long-running service.

**Team compute.** Organization admins compose grids from dedicated or connected servers. Developers use accessible grids for their apps. Quotas and fair-share turns count against each app’s creator; sharing an app does not change who is counted.

## Work through the interface that fits

The [portal](https://portal.kinesis.network) provides interactive configuration and diagnostics. The [REST API](/api) supports scripts and integrations. [MCP](/mcp) makes supported operations available to agents.

Start with [Deploy an app](/getting-started/running-llms-with-kinesis-network), or read [Compute options](/getting-started/running-llms-with-kinesis-network/understanding-compute-sources) before choosing capacity.


# Deploy an app

Choose a source, configure your app and verify its first deployment.

An app is a container workload inside a project. You can start with a curated template, a container image or a GitHub repository. The same concepts apply to model serving, web services and background jobs.

## Choose your starting point

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>App Gallery</strong></td><td>Use a curated configuration and customize it for your workload.</td><td><a href="/getting-started/running-llms-with-kinesis-network/creating-apps-with-app-gallery">Deploy from the App Gallery</a></td></tr><tr><td><strong>Your image or repository</strong></td><td>Configure a container image or build from GitHub with the app wizard.</td><td><a href="/getting-started/running-llms-with-kinesis-network/creating-an-app-from-scratch">Create a custom app</a></td></tr><tr><td><strong>Automation</strong></td><td>Create the project and app from scripts or an MCP client.</td><td><a href="/api">REST API</a></td></tr></tbody></table>

{% stepper %}
{% step %}

## Create or choose a project

Open the [portal](https://portal.kinesis.network), select the organization from the account menu and open **Projects**. Admins and Developers can create projects; apps belong to that organization. See [Create a project](/getting-started/running-llms-with-kinesis-network/creating-a-project).
{% endstep %}

{% step %}

## Configure the app

Choose **Create App** and work through **Source**, **Runtime**, **Network**, **Execution** and **Resources**. Review environment variables, the container port and the compute requirements before launching.

For a model server, check the model's GPU memory needs and any registry or model-download credentials. For a repository, confirm the branch, build context and Dockerfile.
{% endstep %}

{% step %}

## Choose compute and launch

Use [serverless CPU capacity or a grid](/getting-started/running-llms-with-kinesis-network/understanding-compute-sources). Select an accessible grid and check capacity and the app creator’s quotas. If the organization has disabled serverless capacity for Developers, use a grid or ask an admin.

GPU workloads require a grid; serverless GPUs are currently paused. Small supported LLM templates can offer a CPU trial in the gallery.

Launching starts deployment and can consume the organization’s credits. A repository first goes through a build; a dedicated grid may need to acquire machines before the app starts.
{% endstep %}

{% step %}

## Verify the endpoint

Open the app's status, logs and metrics. Use the endpoint shown by Kinesis once it is ready. The container must listen on the configured port and bind to `0.0.0.0` to accept incoming traffic.

If deployment stalls, follow [Monitor and troubleshoot](/getting-started/running-llms-with-kinesis-network/monitoring-apps).
{% endstep %}
{% endstepper %}


# Create a project

Create a project in your organization and choose who can access it.

A project groups related apps inside an organization: a service and its workers, an experiment, or a team's environment. The organization owns the project and pays for its apps. The person who creates it is its **creator**.

## Create the project

1. Sign in to the [portal](https://portal.kinesis.network). Choose the intended organization from the account menu at the bottom of the sidebar.
2. Open **Projects** and choose **Create Project**. You need an Admin or Developer organization role.
3. Give it a useful name, such as `inference-staging`, and choose **Private** or **Everyone at your organization**. Private is the default.
4. Create the project, then choose a gallery template or configure your first app.

An organization's Projects URL includes its permanent slug: `/dashboard/<org-slug>/projects`. The list groups private projects you created, organization-wide projects and other accessible private projects by creator. Filter and sort choices are remembered per organization. Inside a project, apps can be filtered by creator and status.

## Work with a team

Private projects are visible to organization admins, their creator and members added through **Share**. Organization-wide projects are visible to all members within their roles. Apps inherit the project's visibility; a member can also be added to just one app.

Admins and the project's creator manage access and visibility. Developers edit accessible apps with Developer access and delete projects/apps they created. Viewers can inspect, with no terminal or secret values. A resource grant never raises an organization role.

Deleting a project also deletes its apps. A Developer must have created every app inside it; otherwise ask an admin. REST and MCP deletion require all apps to be stopped first.

Removing the creator from the organization does not delete the project or stop its apps. See [Members and roles](/organizations/members) and [Sharing and visibility](/organizations/sharing).

## Move to another organization

An admin of both organizations can use **Move to Another Org** from the project card menu. The preview includes its apps, their grids and the source organization’s servers in those grids. Resolve blockers before confirming. See [Move resources](/organizations/move-resources).

## Automate project creation

Use `POST /projects?org=<slug>` with `name` and optional `visibility: "private"` or `"org"`, or MCP `create_project` with an `org` argument. A credential needs `projects:write` and the person must be an Admin or Developer. Keys limited to specific projects cannot create another project.

Use MCP `update_project` to change the name or visibility. The former action name `rename_project` has been replaced. See [Projects API](/api/projects).

[Deploy from the App Gallery](/getting-started/running-llms-with-kinesis-network/creating-apps-with-app-gallery) · [Create a custom app](/getting-started/running-llms-with-kinesis-network/creating-an-app-from-scratch)


# Deploy from the App Gallery

Start from a curated app configuration and choose how to launch it.

The App Gallery provides starting configurations for supported workloads. A template supplies an app configuration that you can launch or customize for your own environment.

{% stepper %}
{% step %}

### Open app creation

Choose the intended organization, open a project where you have Developer or Admin access and select **Create App**. For a new project, the portal takes you directly to this flow.
{% endstep %}

{% step %}

### Choose a template

Select **Select Template from Gallery**, then choose a workload. **Choose Custom Model** comes first for an Ollama model of your choice. When no grid is available, templates that can run on serverless CPU are ordered first. The vLLM multi-GPU template category is currently hidden in both the gallery and templates API. Review the selected model, image and resource requirements.
{% endstep %}

{% step %}

### Review the compute target

Select serverless CPU capacity or an available grid. GPU workloads need a grid; serverless GPUs are currently paused. Grid quotas count against the app’s creator. If serverless capacity is disabled for Developers, Quick Launch needs an accessible grid; ask an admin when none is available.
{% endstep %}

{% step %}

### Launch or customize

**Quick Launch** submits the preconfigured app with the selected compute target. Use it when the settings already match your needs.

**Customize First** returns you to the configuration wizard. Review ports, environment variables, hardware and deployment location, then launch from **Resources**.
{% endstep %}
{% endstepper %}

{% hint style="warning" %}
Launching an app can consume the organization’s credits or use dedicated capacity. Review the selected target and resource settings before choosing Quick Launch.
{% endhint %}

## Try a small model on CPU

Supported small Ollama models, up to about 9B parameters, offer **Try on CPU**. For a GPU template without a GPU grid, the portal offers **Start on CPU now** when eligible, **Set up a grid**, or **Not now**. CPU replies can be slower than GPU replies. The organization must have credit and allow the member to use serverless; otherwise an admin needs to resolve the credit or compute restriction. A CPU trial changes the configuration to use no GPU.

When using `list_templates`, read `notes` before passing a template to `create_app`. GPU templates do not include a grid and need `config.grids.ids`; copying their configuration unchanged will not create a serverless GPU app.

## Confirm that the app is ready

Open the app detail page. Follow its status, inspect startup logs and check the endpoint once the workload is ready. Model downloads and container startup can take time; an accepted launch does not mean that the service is immediately available.

If a template needs a token, password or other secret, configure it before sending production traffic. Do not put secrets in screenshots or support messages.

[Understand compute options](/getting-started/running-llms-with-kinesis-network/understanding-compute-sources) · [Monitor and troubleshoot](/getting-started/running-llms-with-kinesis-network/monitoring-apps)


# Create a custom app

Configure a container image or GitHub repository in the five-step app wizard.

Select the intended organization, open a project you can edit and select **Create App**. You need an Admin or Developer role with access to that project. The wizard groups configuration into five steps.

| Step          | Configure                                                                            | Check before continuing                                                    |
| ------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| **Source**    | Container image or GitHub repository                                                 | Image tag and registry access; or branch, Dockerfile and context directory |
| **Runtime**   | Environment variables, commands, health checks, shared memory and persistent storage | Required secrets and the process the image should run                      |
| **Network**   | Exposed ports, allowed IPs and domains                                               | Container port, protocol and listener binding                              |
| **Execution** | Service or task execution and restart behavior                                       | Whether the workload should stay running or finish                         |
| **Resources** | Serverless requirements or grids, placement and scale                                | GPU memory, RAM, storage, instance bounds and capacity                     |

## Container images

Use an image reference the runtime can pull. Supply credentials for a private registry. Environment variables marked as secrets are write-only: API reads do not reveal their values.

Override the image's command or entrypoint only when the workload requires it. A service that exits immediately may have the wrong startup command even when its image builds successfully.

## GitHub repositories

Connect the Kinesis GitHub App to the account or organization containing the repository, then select the repository and branch. Confirm the Dockerfile and build context. The portal can use an inline Dockerfile override when needed.

Repository deployments go through import, image build and deployment. Open **Deployments** to follow progress and inspect builder logs. Automatic deployment can rebuild on pushes to the configured branch when enabled.

The public API expresses this source as `source: { kind: "github", installationId, repo, ... }`. The build supplies `config.image`, so do not set a separate image for a repository app.

## Roll back a repository deployment

Open **Deployments** and choose an earlier build with a stored image. **Roll back** reuses that image without rebuilding it; it is unavailable when the image is gone. A running app redeploys; a stopped app uses the image on its next start. Wait for an active build to finish first. The next build, including an automatic build after a push, replaces the rollback. See [Builds API](/api/builds).

## Network and readiness

The configured port is the port inside the container. Public listener ports depend on the selected protocol: fixed HTTP/HTTPS listeners use 80/443, while other load-balanced protocols may allocate a public port. Use the returned endpoint instead of assuming they are the same.

Bind the service to `0.0.0.0`. A service listening only on localhost cannot receive the app's incoming traffic.

## Resources and persistence

GPUs run on grids only. GPU controls are available for Grid apps; serverless placement is CPU-only. An existing serverless app with a GPU requirement must move to a grid or drop that requirement before its next configuration save.

Use the model or application's actual resource requirements to choose memory and GPU capacity. Shared memory is a separate setting from RAM. Named persistent volumes require grid-backed apps; check the volume mount paths and declared sizes.

Review the app creator’s grid quota and available capacity before launch. A quota refusal may require lowering minimum instances, stopping another of the creator’s apps or asking an organization admin to adjust the policy. If serverless capacity is disabled for Developers, select an accessible grid.

Next: [Compute options](/getting-started/running-llms-with-kinesis-network/understanding-compute-sources), [Monitor and troubleshoot](/getting-started/running-llms-with-kinesis-network/monitoring-apps), or the [Apps API](/api/apps).


# Compute options

Choose between serverless capacity and compute managed through grids.

Choose compute for the organization’s workloads. Admins manage servers and grids; Developers deploy to accessible grids or use serverless capacity when enabled. Viewers inspect resources.

| Option                      | How it works                                                                 | Good starting point for                                           |
| --------------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| **Serverless CPU**          | Kinesis places CPU workloads on managed capacity matching their requirements | Getting started without managing servers                          |
| **Dedicated cloud servers** | Provision machines and compose them into grids                               | Workloads needing selected machine types and capacity you control |
| **Your infrastructure**     | Connect machines and use them through grids                                  | Bringing existing compute into the same deployment workflow       |

## Serverless capacity

Serverless GPUs are currently paused. Serverless placement is CPU-only; configure RAM, storage and region requirements. GPU apps must target a grid. The runtime selects eligible CPU capacity. Tight filters reduce the machines that can run the app; a valid configuration does not guarantee capacity is immediately available.

Serverless usage is metered at resource level. CPU activity, memory, storage and network usage can contribute separately to cost. An idle CPU does not imply every other resource is free. Organization admins use Cost and the current [pricing calculator](https://portal.kinesis.network/pricing-calculator) for your workload.

Admins can turn off serverless capacity for Developers under **Organization → Settings → Compute**. Existing running apps keep running, but Developers must target a grid for new starts, rebuilds and changes that need new serverless machines. Admins retain serverless access.

## Dedicated servers and grids

A server is a machine. A grid is the logical group your app targets. A grid can hold several servers, and an app can target more than one grid.

As an organization admin, open **Servers** in the portal to provision or connect machines, then compose them into grids. Developers select an accessible grid in the app’s Resources step. Organization URLs include the slug, for example `/dashboard/acme/my-servers`; the sidebar label is **Servers**.

Dedicated compute is billed through the servers. Hibernation stops compute billing while retaining the disk and its storage cost. A stopped app does not, by itself, confirm that its server is hibernated; inspect the server state and the grid's configured lifecycle behavior.

GPU configurations need at least one grid ID. In the API, `hardware.gpuCount > 0` with no `grids.ids` returns `serverless_gpu_unavailable`. Move an older serverless GPU app to a grid, or remove its GPU requirement, before saving changes.

## Placement and scale

Hardware and location filters combine to restrict eligible machines. More instance capacity requires enough matching resources. Check placement errors and grid capacity when an app has a running target state but no running container.

Grid policies can also cap GPUs, apps and running containers per person, counted against the app’s creator, reclaim idle capacity or queue work fairly under contention. See [Grids and shared capacity](/getting-started/running-llms-with-kinesis-network/understanding-grids).

## Marketplace capacity

The API and MCP catalog can browse spot capacity by hardware and location. Renting marketplace capacity is not exposed by the public operations documented here. Contributing hardware follows the separate [provider guide](/getting-started/kinetization-of-idle-compute).


# Grids and shared capacity

Organize the organization's compute and control how people share capacity.

A grid groups servers that applications can target. It belongs to an organization. Its admins manage the machines, policies, visibility and sharing; developers deploy apps to grids they can access, and viewers inspect them.

## Use or build a grid

Choose one or more grids in an app's **Resources** settings. Check the hardware, available capacity and the app creator's quota before launch. An app targets a grid through `config.grids.ids` in the public API.

Admins provision dedicated machines in **Servers** or attach the organization's existing infrastructure, then add them to a grid. They can also configure marketplace capacity in the portal. Only admins change grid composition or policies. Shared access is for use, not management.

A grid can be **Private** or visible to **Everyone at your organization**. Admins add members through Share. See [Sharing and visibility](/organizations/sharing).

## Quotas belong to people

Grid quotas count against **the person who created the app**, whoever starts or changes it. Everyone is counted, including admins, unless an admin explicitly exempts a person or app.

| Limit     | What is counted on the grid                                                 |
| --------- | --------------------------------------------------------------------------- |
| GPUs      | GPU capacity held by one person's apps; a GPU slice counts as one GPU       |
| Apps      | Distinct apps with live placements; an app counts once on each grid it uses |
| Instances | Running containers on that grid's machines                                  |

Stopped apps do not count. An app targeting several grids can place containers across grids with remaining room. The portal pre-check refuses configurations that cannot run at all: no targeted grid has app room, or their combined instance room is below the app's **minimum** instances. Runtime limits apply to live placements, not the declared maximum scale.

Admins configure **Policies → Quotas** in the portal. Each cap has its own switch. The public API and MCP do not expose quota-policy management.

## Read quota and scheduling signals

| Signal                             | Meaning and next step                                                                            |
| ---------------------------------- | ------------------------------------------------------------------------------------------------ |
| `START_BLOCKED` / `QUOTA_EXCEEDED` | The creator has no room under a quota; free capacity, lower the minimum or ask an admin          |
| `UpdateDeferred`                   | A larger GPU request is on hold until quota permits it                                           |
| `ScaleCapped`                      | The app runs on fewer instances than it requires because app or instance quota limits placement  |
| `EvictionPending`                  | Newest placements exceed a lowered cap and will stop after the grace period                      |
| `OverCapKept`                      | A stateful or restart-disabled app exceeds a cap but is warned rather than automatically stopped |

Lowering a cap can stop newer over-limit placements after the configured grace period, default ten minutes. Zero means the next check. Stateful and restart-disabled apps are warned rather than stopped automatically.

**Fair share** gives people turns when a grid is full, counting by app creator. Apps queue, give up placements after their turn and can queue again. **Idle reclaim** can remove idle workloads after a warning. **Hold behavior** determines whether idle marketplace capacity is retained or released.

Separate **Container Limits** apply to every container, including quota-exempt people and apps. Some changes restart containers, and lowering memory below usage can stop an app. Review the portal's affected-app confirmation when changing those limits.

## Move a grid

Admins of both organizations can move an unused grid from its **Settings**. A server can move from its actions menu only when it is in no grid. The dialog shows blockers and the complete transfer. See [Move resources](/organizations/move-resources).

## Operate and inspect

Grids show their creator and machine counts, with filters remembered per organization. Servers are grouped by grid by default, with an **In use by** column and filters for source, state and whose apps are running. Admin server actions appear in the organization's audit log.

Inspect an app's quota lines, runtime errors, queue position and History when placement stalls. The [monitoring guide](/getting-started/running-llms-with-kinesis-network/monitoring-apps) explains where to look.

Deleting a grid can stop apps that have no remaining target. Apps with other usable grids can continue there. Review the affected apps and retarget them before removing capacity.

[Grids API](/api/grids) · [Servers API](/api/servers)


# Monitor and troubleshoot

Read status, choose the right logs and investigate deployment or runtime failures.

Open an app to inspect its status, endpoints and operational tabs. Image pulls, builds and machine provisioning can take time; use the reported state to decide what to inspect next.

| Tab             | Use it for                                                              |
| --------------- | ----------------------------------------------------------------------- |
| **Logs**        | Container output, separated by server when there are multiple instances |
| **Terminal**    | An interactive shell in a running container                             |
| **Metrics**     | CPU, GPU, memory and other usage over time                              |
| **History**     | App actions and platform events affecting placement or execution        |
| **Deployments** | Repository import, image-build progress and builder logs                |
| **Config**      | Source, runtime, network, execution and resource settings               |

Monitoring covers resources you can access in the current organization. A **Person** filter appears when more than one member has apps, alongside project, app and node filters. Viewers cannot open a terminal or read secret values. Organization admins use the [audit log](/organizations/settings) to trace member, server and resource changes.

## Follow deployment state

| State or symptom              | What to inspect                                         |
| ----------------------------- | ------------------------------------------------------- |
| Building                      | Deployment step and builder logs                        |
| Downloading or starting       | Image size, pull progress and startup behavior          |
| Provisioning                  | Grid capacity, servers and pending orders               |
| Start blocked                 | Runtime errors, balance and placement requirements      |
| Error or repeated exits       | Runtime logs, command, environment and restart policy   |
| Stopped with a running target | Matching capacity, placement filters and runtime errors |
| Queued on a shared grid       | Fair-share position and available capacity              |

## App runs but the endpoint does not respond

Check that the service binds to `0.0.0.0`, uses the configured container port and supports the selected protocol. Use the endpoint Kinesis returns: some protocols allocate a public port different from the container's port. In API status, an endpoint with a null address is not wired yet.

App, build and server logs are the producing process’s raw output. They may contain sensitive values even when app configuration hides secrets. Limit the time range and redact excerpts before sharing them.

## Build fails before the app starts

Read builder logs rather than runtime logs. Confirm the image's base tag, package installation steps, build context and Dockerfile path. For repository apps, correct the source settings or Dockerfile, then trigger a new build. A successful image build is followed by deployment; continue checking app status afterward.

To restore an earlier repository image, use **Roll back** in Deployments while the image is still stored. If it is unavailable, build that commit again. Rollback does not rebuild or restore an old environment/configuration; the next build replaces the image again.

## Grid quota or serverless refusal

Quota usage belongs to the app’s creator. `QUOTA_EXCEEDED` can leave an app start-blocked; `ScaleCapped` means it runs below the required instance count. Lower minimum instances, free another of the creator’s placements or ask a grid admin. Lowered caps can warn and then stop newer placements after the grace period. See [grid policies](/getting-started/running-llms-with-kinesis-network/understanding-grids).

For `serverless_gpu_unavailable`, select a grid or set `hardware.gpuCount` to 0. This is separate from the organization’s serverless permission.

For `serverless_disabled`, select an accessible grid or ask an organization admin. Adding API scopes does not bypass this setting.

## App stops or loses capacity

Inspect runtime errors and History. Repeated startup failures, an idle-reclaim policy, a fair-share turn ending, quota enforcement, a security stop or a manual stop can produce different outcomes. A grid may still be filling even while another instance is running. A `SECURITY_STOPPED` platform event names suspicious activity detected by the security agent; inspect that event before deciding whether to restart.

## Automate inspection

Use REST status, logs and usage endpoints, or MCP `get_app_status`, `get_app_logs` and `get_app_usage`. Runtime logs and build logs require `monitoring:read` in addition to any resource-specific scope listed in the reference. App-usage queries support ranges of at most 30 days.

See the [Apps reference](/api/apps), [Builds reference](/api/builds) and [agent workflows](/mcp/workflows).

When asking [support](/support) for help, include the organization slug, project/app ID, time of the failure, request ID when available and a short log excerpt with secrets removed.


# Contribute compute

Prepare, onboard and operate hardware contributed to the Kinesis network.

Bring eligible idle hardware to the Kinesis network so it can serve workloads when available. The provider guides cover expectations, onboarding, performance and earnings, and removing a host.

{% hint style="info" %}
Provider access and hardware eligibility are subject to approval. Start with the requirements and contact the Kinesis team if you need access or help assessing a machine.
{% endhint %}

The organization contributes the machines and receives the earnings. Its admins use the Provider Hub after supply access is enabled and the organization accepts the supply terms. Select the intended organization before onboarding or changing availability.

## Provider path

1. [Understand kinetized compute](/getting-started/kinetization-of-idle-compute/what-is-kinetized-compute): how contributed capacity fits into the network.
2. [Review provider expectations](/getting-started/kinetization-of-idle-compute/expectations-from-providers): operating conditions and responsibilities.
3. [Onboard your compute](/getting-started/kinetization-of-idle-compute/onboard-your-compute): installation and machine registration.
4. [Review performance and earnings](/getting-started/kinetization-of-idle-compute/reports-performance-and-earnings): understand reports for your hosts.
5. [Offboard a machine](/getting-started/kinetization-of-idle-compute/offboard-your-compute): remove capacity when it is no longer available.

If you want to run your own applications on infrastructure you control, see [Compute options](/getting-started/running-llms-with-kinesis-network/understanding-compute-sources) and [Grids](/getting-started/running-llms-with-kinesis-network/understanding-grids).

[Contact support](/support)


# What is Kinetized Compute?

Provider resources and earnings belong to the selected organization. Only its admins manage the Provider Hub; choose the intended organization before operating on its fleet.

Kinetized compute is a **whole machine** you list on the Kinesis Network. Customers run their containers on it. You are paid for the hours it is **Rented**, not for hours it sits **Idle**.

Each listed machine has:

* An **availability window** — until a date you set, or until you change it
* A **notice period** — shown as **24 hrs** unless you change it
* **You earn** — the hourly payout Kinesis sets from the machine’s specs

Customers see hardware, location, and a Kinesis price. They do not see who you are. You see **Idle** or **Rented**. You do not see who is renting you.

On Fleet:

* **Needs setup** — the agent is on the host; the machine is not listed yet
* **Idle** — listed, not rented right now
* **Rented** — a Grid is using it; earnings accrue

Idle after you have listed is normal. You stay listed. You do not need to list again. You earn only while **Rented**.


# Expectations From Providers

Provider resources and earnings belong to the selected organization. Only its admins manage the Provider Hub; choose the intended organization before operating on its fleet.

Keep the machine powered, on the network, and running the Kinesis agent for the window you listed.

You can **extend** the window at any time, including while it is **Rented**. Extending is free.

While **Rented**, **You earn** is locked. Availability & Pricing says: shortening or withdrawing a committed window during a lease is a contract violation with a monetary consequence.

**Pause** while rented stops new rentals. The current rental continues. **Remove** becomes available once that rental ends.

You do not pick the customer or the app.


# Onboard Your Compute

Provider resources and earnings belong to the selected organization. Only its admins manage the Provider Hub; choose the intended organization before operating on its fleet.

Windows / WSL uses the Dynamo Spark desktop app and always uses Kinesis Remote Reach. Start linking in Dynamo Spark; an organization admin authorizes the PC in the portal and selects the organization it will supply. If the app asks for a token, use the download option or Already installed? Get a provision token on Add machines to obtain a one-hour, one-machine token. The PC appears in the organization’s provider Fleet, not Servers.

Go to [Fleet](https://portal.kinesis.network/dashboard/provider/fleet). If the list is empty, Add machines is at the top right and in the empty state — same action.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FnotV2UnWrJPErKVHWUQo%2FScreenshot%202026-08-25%20at%2015.35.28.png?alt=media&amp;token=93d1aff7-930b-485d-a3f0-0568cff71728" alt=""><figcaption></figcaption></figure>

In Add machines, choose Linux (Ubuntu) or Windows / WSL. The Linux connection steps follow below. Answer the connection questions before copying the install command.

Choose Home or office, Datacenter, or Not sure. Home or office and Not sure recommend Kinesis Remote Reach. Datacenter asks whether you have a public IP and control the firewall: Yes recommends Direct connection; No or Not sure recommends Remote Reach. You can change the recommendation.

Kinesis Remote Reach carries TCP app traffic through a WireGuard tunnel and needs outbound UDP to Kinesis gateways on dynamically assigned ports. Allowing only HTTPS or UDP 51820 is insufficient. No inbound app-port forwarding is needed, but UDP app endpoints are unsupported. Kinesis does not install a host firewall in this mode; keep your own firewall configured.

Direct connection needs a reachable public address and upstream inbound network access. The portal’s network instructions open automatically for datacenter Direct connections. App host ports are dynamic: the current guide requires upstream TCP and UDP ports 1–65535, not just the port inside your container. These broad rules can expose other services, so review them with your network administrator. The installer cannot configure your router or upstream firewall.

Before installing with Direct connection, configure and test SSH on TCP 22; the installed host firewall allows that port. The connection guide gives configuration advice and does not test connectivity.

Copy the generated command and run it on the host. Reuse it only on machines with the same connection setup. Changing the connection method after installation re-registers the machine as a new entry; add that entry back to any grids and remove the old entry when no longer needed. Both Direct and Remote Reach machines can be listed as marketplace capacity.

You can leave this page. Use **Go to fleet**.

Once the host is in, the row shows **Needs setup**. The **Needs setup** card shows **1** and **Not listed yet**. Click **Set them up** on the banner or **Set up** on the row.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2F2YeQ9lpjdJ6oGkV7gHHK%2FScreenshot%202026-08-25%20at%2015.37.01.png?alt=media&amp;token=c5a32227-4419-497e-9e84-2b16f7a8dec8" alt=""><figcaption></figcaption></figure>

Pick **Until I change it**, or **Until a date**. Leave **Notice period** at **24 hrs** unless you need something else. **You earn** is filled in from the machine’s specs. Click **List as spot capacity**.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FPlLFqK9hCSkmHO801vMf%2FScreenshot%202026-08-25%20at%2015.38.39.png?alt=media&amp;token=4fccd6b2-9d1b-430c-aaf4-63f2a932b3f3" alt=""><figcaption></figcaption></figure>

**Until a date** opens a date picker.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FuFNIWpuwsdkLFgyIepBB%2FScreenshot%202026-08-25%20at%2015.39.27.png?alt=media&amp;token=885c51f7-87b1-4a2b-9559-1c63513f6056" alt=""><figcaption></figcaption></figure>

Once you list it, the row shows **Idle**. The machine can be matched. Earnings start when the status becomes **Rented**.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FMQZ0kn9FPhVjYbapRkXs%2FScreenshot%202026-08-25%20at%2015.40.05.png?alt=media&amp;token=19b5fbf8-8650-4e76-ab81-159008a2852c" alt=""><figcaption></figcaption></figure>


# Reports, Performance, and Earnings

Provider resources and earnings belong to the selected organization. Only its admins manage the Provider Hub; choose the intended organization before operating on its fleet.

Fleet shows heartbeat separately from listing status: Online, Last seen, or Installing, waiting for first heartbeat. Unlisted machines with no heartbeat in the last five minutes appear under Not reporting. A sleeping or powered-off machine may appear there until it returns; reinstalling the agent creates a new entry and can leave the old entry behind. Check heartbeat before treating a Needs setup entry as a live machine.

**Fleet** is the live view: **Machines, Online, Rented now, Needs setup**. When a Grid is using the host, **Rented now** is **1** and the row is **Rented**. If it goes back to **Idle** without Pause, you are still listed.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FkKo8g1fJG8IcPaozZXAE%2FScreenshot%202026-08-25%20at%2015.42.34.png?alt=media&amp;token=ee720c87-b359-4dac-8c05-d500b02c4b88" alt=""><figcaption></figcaption></figure>

[Availability & Pricing](https://portal.kinesis.network/dashboard/provider/availability-pricing) is the table of window, notice, **You earn**, and **Withdraw**.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2Fobe7ms7R8BvWe7OjJL6j%2FScreenshot%202026-08-25%20at%2015.43.20.png?alt=media&amp;token=1d4fec63-1781-4aa8-a742-4b17e882c58a" alt=""><figcaption></figcaption></figure>

[Earnings](https://portal.kinesis.network/dashboard/provider/earnings) accrue hourly while machines are **Rented**. Payouts are processed monthly.

If nothing has been rented yet, you see **No earnings yet**.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2F9zyd0fBzTlbRCjy01asg%2FScreenshot%202026-08-25%20at%2015.44.00.png?alt=media&amp;token=ebb67cf7-88cc-4901-93a2-ae968d17b0f5" alt=""><figcaption></figcaption></figure>

After rented hours bill, you see **Accrued this period, Balance owed, Paid out to date,** and **Next payout**.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2F6E9NL93GcIVpUw4px8WN%2FScreenshot%202026-08-25%20at%2015.44.57.png?alt=media&amp;token=ed079ca2-4c27-4877-8fd5-841ec06e06f3" alt=""><figcaption></figcaption></figure>


# Offboard Your Compute

Provider resources and earnings belong to the selected organization. Only its admins manage the Provider Hub; choose the intended organization before operating on its fleet.

Open the machine on Fleet.

* **Pause** — withdraws it from spot. It stays onboarded. Relist with **Set up** → **List as spot capacity**.
* **Remove** — withdraws the listing and uninstalls the Kinesis agent. To use the host again, run the install command.

If the machine is still **Rented**, Pause only stops new rentals. Removal becomes available once the current lease ends.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FR7GqO96HUOOjDY696LFy%2FScreenshot%202026-08-25%20at%2015.45.54.png?alt=media&amp;token=e8d581d0-81a7-4ed3-af9e-307b0a9641fe" alt=""><figcaption></figcaption></figure>

When the row is **Idle**, **Pause** opens this confirm dialog. Confirm with **Pause anyway**.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FFp7gGenBn9Uf1OZThZTY%2FScreenshot%202026-08-25%20at%2015.47.00.png?alt=media&amp;token=ec6d6655-da2f-476e-8253-574febd50c93" alt=""><figcaption></figcaption></figure>

Fleet then looks like **Needs setup** again.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2F2JYiHLNWZaitl4ADM0gS%2FScreenshot%202026-08-25%20at%2015.47.38.png?alt=media&amp;token=0dc7fcf9-ea83-41a9-985e-3564a6667780" alt=""><figcaption></figcaption></figure>

To take the host off Kinesis, use **Remove this machine from Kinesis instead**.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FApZUY6QO5GeP9LcSztBg%2FScreenshot%202026-08-25%20at%2015.48.14.png?alt=media&amp;token=4a66bfca-724b-4331-a351-6b18ea18b7ca" alt=""><figcaption></figcaption></figure>

Confirm with **Remove machine**.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2F12aLswfzjewJ0pJQA3k5%2FScreenshot%202026-08-25%20at%2015.48.55.png?alt=media&amp;token=3b34b13e-0dc6-40ae-b7bc-447271035725" alt=""><figcaption></figcaption></figure>

Fleet is empty.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2Fwjy64oG64TvEcBcV2I1t%2FScreenshot%202026-08-25%20at%2015.49.23.png?alt=media&amp;token=a86a20ba-22bb-4096-9a10-f1339fd1d2e4" alt=""><figcaption></figcaption></figure>


# Organizations

Bring your people, projects, compute and billing into one organization.

An organization owns your team's projects, apps, grids and servers, and pays for their usage. Your account is your identity; your role in each organization determines what you can do there.

```mermaid
flowchart TD
  O[Organization] --> M[Members and roles]
  O --> P[Projects]
  P --> A[Apps]
  O --> G[Grids and servers]
  O --> B[Balance and billing]
  A --> C[Serverless capacity or accessible grids]
```

## Start or join

A regular signup creates an organization for you. Signing up through an invitation joins the inviting organization without creating another one. You can belong to several organizations and own up to five.

To create one later, open the account menu at the bottom of the sidebar and choose **Create organization**. Give it a name and URL slug. You become its owner and an admin. The name can change; the slug is permanent.

Invitations appear in email, the notification bell and the [Organizations page](https://portal.kinesis.network/dashboard/orgs). Sign in with the invited email address to join or decline. Invitation links expire after seven days.

Organizations with [SSO](/organizations/single-sign-on) can also admit people through their identity provider. First sign-in uses a valid invitation’s role or the organization’s default; existing members keep their roles.

## Switch organizations

The current organization appears under your name at the bottom of the sidebar. Open that account menu and select another organization. Its projects, compute and billing become the current workspace.

Organization pages include the slug in the URL, for example `/dashboard/acme/projects`. A saved link continues to identify the same organization after a rename.

| In the organization          | What lives there                                                                     |
| ---------------------------- | ------------------------------------------------------------------------------------ |
| **Projects**                 | Projects and their apps, with creator and visibility filters                         |
| **Servers**                  | The organization's machines, their grids and who is using them                       |
| **Grids**                    | Capacity available to applications; admins manage it                                 |
| **Monitoring**               | Usage for resources you can see, with a person filter when several members have apps |
| **Cost**                     | Organization spend and resource breakdowns; admins only                              |
| **Organization → Billing**   | Credits, payment methods and invoices; admins only                                   |
| **Organization → Members**   | People, roles and invitations                                                        |
| **Organization → Settings**  | Name, compute and invitation controls, single sign-on and SSH keys                   |
| **Organization → Audit log** | One year of recorded activity; admins only                                           |

Your profile, sign-in methods, API keys and connected apps remain in [Account settings](https://portal.kinesis.network/dashboard/account), reached from the account menu. They are the same identity across organizations.

## Work as a team

| Guide                                             | Learn how to                                                            |
| ------------------------------------------------- | ----------------------------------------------------------------------- |
| [Members and roles](/organizations/members)       | Invite colleagues, change roles, transfer ownership and leave           |
| [Sharing and visibility](/organizations/sharing)  | Choose private or organization-wide access and add members              |
| [Organization billing](/organizations/billing)    | Understand the shared balance, costs and admin responsibilities         |
| [Single sign-on](/organizations/single-sign-on)   | Connect an identity provider and require SSO for browser access         |
| [Move resources](/organizations/move-resources)   | Transfer a project, grid or server between organizations you administer |
| [Settings and audit log](/organizations/settings) | Control serverless use and invitations, and trace changes               |

Resources stay with the organization when their creator leaves, and their apps keep running. Admins of both organizations can [move projects, grids or servers in the portal](/organizations/move-resources). Merging whole organizations remains support-assisted; [contact Kinesis support](/support).

## Automate in the right organization

An API key or connected app acts as you, with your role in each organization it can reach. Use `GET /orgs` or MCP `list_orgs` to choose the organization. Organization-level calls take its slug as REST `?org=` or an MCP `org` argument; calls on a project, app or grid resolve it from that resource.

Start with [Authentication and scopes](/api/authentication), the [Organizations API](/api/organizations), or the [MCP organization actions](/mcp/tools/organizations).


# Members and roles

Invite your team, assign roles and manage the organization's ownership.

Open **Members** in the sidebar's **Organization** group. Each person has one role in this organization. Their role in another organization can be different.

## Choose a role

| Capability                             | Admin    | Developer                         | Viewer                   |
| -------------------------------------- | -------- | --------------------------------- | ------------------------ |
| See projects and apps                  | All      | Those accessible to them          | Those accessible to them |
| Create projects and apps               | Yes      | Yes                               | No                       |
| Configure and operate accessible apps  | Yes      | Yes, with Developer access        | No                       |
| Delete projects and apps               | Yes      | Those they created                | No                       |
| Manage private project/app access      | Yes      | Those they created                | No                       |
| Deploy to accessible grids             | Yes      | Yes                               | No                       |
| Manage grids, servers and SSH keys     | Yes      | No                                | No                       |
| Invite people                          | Any role | Developer or Viewer, when enabled | No                       |
| Change member roles or remove others   | Yes      | No                                | No                       |
| Manage billing, settings and audit log | Yes      | No                                | No                       |

A Viewer has read-only access: no terminal and no secret values. Resource grants cannot raise someone above their organization role. A Developer added to a private project as Viewer can only read it unless another applicable access rule gives them more access.

A Developer can delete a project only when they also created every app inside it; otherwise an admin must do it.

The Members page shows roles and join dates. Admins also see last sign-in and the number of projects, apps and grids each person created. These counts are not a cost allocation report.

## Invite a colleague

1. Select **Invite** on Members and enter their email address.
2. Choose a role no higher than your own. Developers can invite only when admins allow it in organization Settings.
3. Send the invitation. The recipient signs in with that email address and accepts the link.

The link is valid for seven days. **Resend**, or inviting the address again, sends a fresh link. Wait at least a minute between sends to the same address. Admins manage all pending invitations; developers manage the ones they sent.

You can also invite from a resource's Share dialog. The person joins the organization and is added to that project, app or grid on acceptance. Existing members are added directly, without an invitation.

Revoking a pending invitation stops its link from working. Lowering a member's role withdraws invitations they sent above their new role. Turning off developer invitations in Settings leaves already-sent invitations valid.

## Join through single sign-on

When [SSO](/organizations/single-sign-on) is enabled, a first provider sign-in joins at a valid invitation’s role, otherwise the configured Developer or Viewer default. Existing members keep their roles. An account created through SSO does not get a separate organization.

## Change a role or remove a member

Admins change roles and remove members from Members. The new access takes effect immediately.

Removing someone ends their access through the portal, API keys and connected apps **in this organization**. Their projects, apps and grids stay with the organization, and their apps keep running. Their access to other organizations follows their memberships there. With SSO enabled, also unassign them at the identity provider: if still assigned, they can rejoin at the default role on their next SSO sign-in.

Anyone except the owner can leave. A departing owner must transfer ownership first. Credentials restricted to this organization lose that access; if no allowed organizations remain, they are revoked.

## Transfer ownership

Every organization has one owner, identified by the **Owner** badge. The owner is always an admin and cannot leave, be removed or change role while they own it.

The current owner selects **Transfer ownership** on Members and chooses another member. That person becomes the owner and an admin if needed. The previous owner remains an admin and can then leave or have their role changed. An organization always retains at least one admin.

## API and MCP

Use `GET /members?org=acme` or `list_members` to discover user IDs. `details=true` adds last sign-in and created counts only for admins whose credential is not limited to projects.

Member reads require `members:read`; invitations, role changes, removal and transfer require `members:write`. These scopes do not override the role rules above. The owner alone can transfer ownership. MCP `remove_member` and `transfer_ownership` require `confirm: true`; removal with your own `userId` leaves the organization.

See the [Members and invitations tool reference](/mcp/tools/members) and [Organizations API](/api/organizations).


# Sharing and visibility

Control which organization members can see and use projects, apps and grids.

Sharing gives members of the same organization access to a private project, app or grid. Invite a new colleague into the organization first, or invite them from the resource's Share dialog so they are added when they join.

## Private or everyone

| Visibility                        | Who can access it                                        |
| --------------------------------- | -------------------------------------------------------- |
| **Private**                       | Organization admins, its creator and members added to it |
| **Everyone at your organization** | All organization members, within their roles             |

Projects are private by default. Choose visibility when creating a project, or change it under the project name as its creator or an admin. Admins choose a grid's visibility in its settings.

Apps follow their project's visibility. You can also add a member to one app without giving them the rest of the project. Access to a project covers its apps.

## Add members directly

Open **Share → People in your organization**, search for members and add them. Access begins immediately; they do not need to accept another invitation.

Admins manage access to every resource. Developers manage access to projects and apps they created. Choose Viewer or Developer access for a project or app; the grant cannot exceed the person's organization role.

Revoking an individual grant does not remove access supplied by another rule. For example, an organization-wide project remains visible to members, and a project grant still grants access to its apps. Change the applicable visibility or project grant if you need to remove that access.

## Share a grid for use

Only organization admins manage grid composition, policies and sharing. A shared grid gives members permission to use its capacity, not to administer it. Developers can deploy apps to accessible grids; Viewers can inspect them.

Grid quotas and fair-share turns count against the person who created the app. Sharing the app does not move its quota usage to the person starting it. See [Grids and shared capacity](/getting-started/running-llms-with-kinesis-network/understanding-grids).

## API values and permissions

Organization roles are `admin`, `developer` and `viewer`. Resource-grant payloads retain the wire values `editor` and `viewer`: **`editor` is Developer in the portal**. Do not send `developer` where a resource-grant schema expects `editor`.

`add_grantees` / `POST /sharing/grantees` takes `resourceType`, `resourceId` and a `people` array of `userId` and `role`. Obtain user IDs with `list_members` / `GET /members`. Grids accept `viewer` only; organization role determines whether the member can deploy or only read.

Sharing uses the resource family's admin scope: `projects:admin`, `apps:admin` or `pools:admin`. The person must also meet the creator/admin rule. Invitation `addTo` needs the matching resource admin scope as well as `members:write`.

Use `list_grantees`, `set_grantee_role` and `revoke_access` to inspect or change grants. To give up your own grant, use `leave_shared_resource` with `account:write`. Older invitation operations remain in the reference for handling existing invitations; new colleagues join through organization invitations.

[REST sharing reference](/api/sharing) · [MCP sharing reference](/mcp/tools/sharing)


# Single sign-on

Connect your identity provider, manage first-sign-in roles and require SSO for organization access.

Single sign-on (SSO) lets your team access a Kinesis organization through its identity provider, such as Okta, Microsoft Entra or Google Workspace. Kinesis uses WorkOS for the provider connection; organization roles and resource permissions remain in Kinesis.

## Set up your organization

1. As an organization admin, open **Organization → Settings → Single sign-on**. If it says **Not set up**, contact Kinesis support to connect your provider.
2. Work with support and your IT administrator to establish an active provider connection and verify your organization's email domain. A domain can route SSO to only one Kinesis organization.
3. Once enabled, return to Single sign-on. The card shows the enabled date, verified email domains, sign-in link and role on first sign-in.
4. Copy the organization's sign-in link and test it before requiring SSO for everyone.

Admins can subsequently use **Set up the connection** and **Verify a domain** to open the hosted setup portal. After IT verifies a new domain there, select **Refresh domains** in Kinesis. Domain changes are not copied into Kinesis until refreshed. If no verified domain remains, the refresh is refused; contact support to resolve the setup or turn SSO off.

Other members can read the SSO details once it is enabled. Only admins change them. Kinesis support enables or disables the provider connection; **Require single sign-on** is a separate organization control.

## Sign in

Use any of these entry points:

* On the [sign-in page](https://portal.kinesis.network/signin), choose **Continue with Single Sign-On** and enter your work email. Its verified domain identifies the organization.
* Use the sign-in link copied from Settings: `https://portal.kinesis.network/sso/<org-slug>`. It goes directly to the provider without asking for an email first.
* Open the Kinesis tile in your identity provider, when your IT administrator has configured one.

The short [SSO link](https://portal.kinesis.network/sso) opens sign-in with the SSO option selected. Complete authentication with your provider, then return to Kinesis. Your email must be on a domain verified for the organization, including when you use its direct link.

## First sign-in and roles

| Person signing in                      | Result                                                                         |
| -------------------------------------- | ------------------------------------------------------------------------------ |
| Existing organization member           | Keeps their current role                                                       |
| Person with a valid pending invitation | Joins at the invitation's role and receives any resource access included in it |
| Person without an invitation           | Joins at the configured **Role on first sign-in**                              |

Admins choose **Developer** or **Viewer** as the default first-sign-in role. Changing it does not change existing members. Grant Admin access through normal member administration or an appropriate invitation.

SSO uses an existing Kinesis account with the verified email when available. A new person gets an account and membership in this organization, without a separate organization of their own. An account created through SSO has no Kinesis password; authentication and its second factor are handled by the provider. Existing verified accounts keep their password and Kinesis two-factor settings. A person can later set a Kinesis password through **Forgot password**.

## Require SSO

An admin who has signed in through the organization's provider can turn on **Require single sign-on** in Settings. The control cannot be enabled from a session that has not completed that organization's SSO, even by the owner.

When required, members need a browser session that signed in through this provider **within the last 24 hours**. Other sessions see a prompt to continue with SSO and return to the page afterward. A member attempting password sign-in with an email on the organization's verified domain is sent to its provider.

The requirement applies to access to this organization, including its resources. It does not sign the person out of every other organization. Turning SSO off and on again invalidates the earlier SSO sign-in for this requirement.

The owner retains a recovery path using a Kinesis password and a second factor. Authorized Kinesis support access also remains possible. Both are recorded in the organization's audit log. Contact support if a provider outage locks the organization out; support can stop requiring SSO.

## API keys and connected apps

Existing API keys and connected apps keep working under their existing scopes, roles and organization limits. They do not need browser SSO renewed every 24 hours. Requiring SSO does not itself revoke them.

Creating a key, changing where a key works, or approving a connected app that reaches an organization requiring SSO checks that the browser session is allowed into that organization. If Kinesis asks for SSO, complete it and repeat the credential setup. You can instead limit the credential to organizations the session can already access. The owner's recovery session follows the access exception described above.

SSO settings are managed through the portal and support. The public REST API and MCP organization-setting actions do not configure SSO.

## Remove access

Remove the person from the Kinesis organization **and unassign them from Kinesis at the identity provider**. While SSO remains enabled, someone still allowed by the provider can rejoin at the default role on their next SSO sign-in. Removing membership ends their key and connected-app access in that organization; their resources remain and apps keep running.

## Troubleshoot sign-in

| Message or situation                                  | Next step                                                                        |
| ----------------------------------------------------- | -------------------------------------------------------------------------------- |
| SSO is not set up                                     | Ask an organization admin to contact support                                     |
| Email is outside the verified domains                 | Use the correct work email; IT can verify the domain and an admin can refresh it |
| Login belongs to another organization                 | Start again from the intended organization's sign-in link                        |
| Sign-in started elsewhere or expired                  | Start a fresh sign-in in the same browser                                        |
| Provider did not complete sign-in                     | Check the provider's app assignment and sign-in result; retry or ask IT          |
| Require SSO is unavailable                            | Sign in through this organization's provider first                               |
| Key creation or OAuth approval returns `sso_required` | Complete SSO for the organizations the credential will reach                     |

[Members and roles](/organizations/members) · [Settings and audit log](/organizations/settings) · [Get support](/support)


# Organization billing

Understand the organization's balance, costs, payments and compute limits.

Each organization has its own balance, payment methods, invoices and billing settings. Usage from its projects, apps and dedicated servers is charged to the organization. Changing who operates an app does not change who pays.

Only **admins** can see Billing, invoices, Cost and the sidebar balance, or manage payments. Other members see a notice when the organization is out of credit and can ask an admin to resolve it.

## Manage funds and invoices

Switch to the intended organization, then open **Organization → Billing**. Admins can add credits, configure auto-reload, manage saved cards, redeem a voucher and inspect transactions. Invoices contain the organization's usage and payments.

Signup credit is granted once per person; creating another organization does not provide another signup grant. Vouchers and the Discord reward are also limited per person and credited to the selected organization.

Billing notices go to the organization's admins and an optional billing contact configured on the payment page. Cost shows breakdowns by project, app, provider and region over a selected period.

## Understand limits and controls

The organization's app-creation and provisioning allowances belong to the organization. Grid quotas are different: each grid can cap the GPUs, apps and running containers held by one person's apps. The app's creator is the person counted, including when another member operates it. Admins are counted unless explicitly exempted.

**Node protection** hibernates dedicated servers when the balance is projected to run out within the configured threshold. Hibernated servers retain storage charges. **Smart Lifecycle** instead responds to idle machines and wakes them when needed.

An organization frozen for a payment problem cannot start apps, add compute or make payments until support resolves it. A frozen personal account affects that person's access across organizations. Removing a member does not stop their apps or end their organization's usage charges.

## Automate billing

Pass `?org=<slug>` to REST billing calls or `org` to MCP billing actions. The credential needs billing scopes and its person must be an admin there. A key limited to projects cannot manage organization billing; the costs operation requires an explicit permitted `projectId` and still requires the person to be an admin.

MCP can inspect balances, costs, invoices and ledger history, manage supported saved-card settings, redeem vouchers and configure node protection. It cannot buy credits or configure auto-reload. MCP money errors describe the entitlement without payment links; an agent should explain the refusal and leave billing to an admin.

REST also supports checkout sessions, saved-card top-ups and auto-reload updates. Consult the [Billing API](/api/billing) for the exact inputs and permissions.


# Settings and audit log

Set organization controls and trace activity in the audit log.

Open **Settings** in the sidebar's **Organization** group. Admins can rename the organization and change its controls. Renaming leaves the URL slug unchanged. Other members can read the name, URL and switches.

## Compute and invitation controls

| Setting                                                      | Enabled                                                            | Disabled                                                                   |
| ------------------------------------------------------------ | ------------------------------------------------------------------ | -------------------------------------------------------------------------- |
| **Kinesis serverless capacity** (`serverlessEnabled`)        | Developers can run apps on Kinesis-managed serverless CPU capacity | Developers must target an accessible grid; admins can still use serverless |
| **Non-admin members can invite people** (`membersCanInvite`) | Developers can invite Developers and Viewers                       | Only admins can send new invitations                                       |

Both settings default to enabled. Disabling serverless capacity does not stop apps already running. It prevents developers from creating, starting, restarting or rebuilding serverless apps, and from changes that need new serverless machines. Select a grid in the app's Resources settings, or ask an admin.

Disabling developer invitations leaves invitations already sent valid. Review and revoke pending invitations separately when needed.

## Single sign-on

Contact support to connect your organization’s identity provider. Once enabled, this Settings section shows its verified domains and sign-in link. Admins choose the first-sign-in role, open the provider setup portal, refresh domains and optionally require SSO. The requirement uses a 24-hour browser sign-in window and does not revoke existing API keys or connected apps. Read the [SSO guide](/organizations/single-sign-on) for setup, recovery and offboarding.

Admins also register the organization's SSH public keys in Settings. Assignment depends on server eligibility; use the [server setup reference](/mcp/tools/server-setup).

Your personal profile, sign-in methods, API keys and connected apps are in [Account settings](https://portal.kinesis.network/dashboard/account), not organization Settings.

## Read the audit log

Admins open **Organization → Audit log** to see who changed the organization's members, settings, projects, apps, grids and servers. The log retains one year of activity, including recorded platform events. SSO setup, domain/default-role/requirement changes, SSO joins, owner recovery sign-ins and support access are recorded, as are resource moves between organizations. Expand a row with a recorded diff to inspect field changes.

The API returns entries in **latest-recorded-first** order. Follow `nextCursor` for older entries. Each entry includes:

| Field                      | Meaning                                                                      |
| -------------------------- | ---------------------------------------------------------------------------- |
| `actor`                    | Person who acted; null for the platform or an account that is gone           |
| `action`                   | Recorded operation, such as `memberInvite`, `appCreate` or `serverProvision` |
| `outcome`, `errorCode`     | Success or error, and a code for a failed action                             |
| `details`                  | Action-specific context                                                      |
| `changes`                  | Field paths, change kinds and before/after values where recorded             |
| `at`, `role`, `viaSupport` | Time, actor's role and whether support acted while signed in as the person   |

Secret changes are marked as redacted; their values are not retained in the diff.

## API and MCP

`GET /org?org=acme` / `get_org` reads settings, caller role and frozen state. `PATCH /org?org=acme` / `update_org` changes the name or compute/invitation switches and requires `org:write` plus an admin role. SSO controls are portal-managed and are not fields on this public operation.

`GET /org/audit?org=acme` / `list_audit_log` requires **`audit:read` specifically**, plus an admin role. `org:write` and `members:read` do not imply audit access. The audit log names members, so grant this scope deliberately.

In compact MCP mode these actions live under `account` and `account_write`. Older connected apps must be re-approved with the new scopes before they can use them. See [OAuth for MCP clients](/mcp/oauth).


# Move resources between organizations

Transfer a project, grid or server to another organization from the portal.

An admin of both organizations can move a project, grid or server in the portal. The move dialog previews what will move, identifies blockers and asks you to type the resource's name to confirm.

| Resource | Where to start                              | What must be true                                                      |
| -------- | ------------------------------------------- | ---------------------------------------------------------------------- |
| Project  | Project card menu → **Move to Another Org** | No app outside the project is running on a grid that will move with it |
| Grid     | The grid's **Settings**                     | No app is using the grid                                               |
| Server   | The server's actions menu                   | The server is not in any grid                                          |

## Review what moves

A project moves with its apps, the grids its apps run on, and the source organization's servers in those grids. The destination organization takes responsibility for the moved resources and pays for the servers from the move onward. Review the dialog's complete resource list and warnings before confirming.

If either organization requires SSO, the browser session must be allowed into it before moving resources.

The move controls remain visible when a move is blocked; open the dialog to see why. Resolve the listed blockers and preview the move again. If the resource set has changed since the preview, Kinesis asks you to review it again.

Both organizations' audit logs record the move. If a move stops partway through, use **Finish the move** to complete it rather than starting a different transfer.

## Apps within one organization

Moving an app between projects in the **same** organization is a separate operation. It is available through the app's move flow, REST `POST /projects/{projectId}/apps/{appId}/move`, or MCP `move_app`. It does not transfer the app to another organization.

Cross-organization moves use the portal; they are not public REST or MCP operations. Merging entire organizations remains support-assisted. [Contact support](/support) for a merge or a move that cannot be completed through the dialog.


# REST API

Automate deployments, compute and billing with a documented REST interface.

Build scripts, CI pipelines and integrations on the Kinesis API.

**Base URL** `https://api.kinesis.network/v1`\
**Authentication** API key in `Authorization: Bearer <key>`\
**Format** JSON; invoice PDF download returns `application/pdf` · OpenAPI 3.1

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Make your first request</strong></td><td>Check your identity, create a project and launch a container.</td><td><a href="/api/quickstart">API quickstart</a></td></tr><tr><td><strong>Authentication and scopes</strong></td><td>Choose permissions and understand shared-resource access.</td><td><a href="/api/authentication">Authentication and scopes</a></td></tr><tr><td><strong>Reliable integrations</strong></td><td>Pagination, rate limits, asynchronous work and safe retries.</td><td><a href="/api/conventions">Request conventions</a></td></tr></tbody></table>

## Organization context

Credentials act as their person in each organization. Discover them with `GET /orgs`. Organization-level calls take `?org=<slug>`; project, app and grid calls resolve the organization from the resource. Scopes never raise your organization role. See [Authentication and scopes](/api/authentication).

## API reference

The reference covers 120 operations and 68 response models from the public OpenAPI specification. Each operation includes request fields, responses, required scopes and errors.

| Resource                                        | Operations                                                                                              |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| [Docs](/api/docs)                               | Built-in guides                                                                                         |
| [Account](/api/account)                         | Identity, credential context and notifications                                                          |
| [Organizations and members](/api/organizations) | Organizations, members, invitations, ownership, settings and audit                                      |
| [Projects](/api/projects)                       | List, create, inspect, update name/visibility and delete                                                |
| [Apps](/api/apps)                               | Create, configure, check placement, move, operate, retain/export/clear logs and inspect usage           |
| [Sharing](/api/sharing)                         | Add organization members, manage grants and handle existing resource invitations                        |
| [Builds](/api/builds)                           | Build history, status, logs, rebuilds, image-availability checks and rollback                           |
| [Domains](/api/domains)                         | Reserve, verify, attach, detach and choose a default                                                    |
| [GitHub](/api/github)                           | Accounts, repositories, branches, commits, contents and Dockerfiles                                     |
| [Templates](/api/templates)                     | App Gallery configurations                                                                              |
| [Grids](/api/grids)                             | Compose capacity, set server priority and start/stop grid apps                                          |
| [Servers](/api/servers)                         | Provision, inspect, read machine logs, power, replace, attach BYOD, manage Smart Lifecycle and SSH keys |
| [Catalog](/api/catalog)                         | Regions, zones and instance types                                                                       |
| [Marketplace](/api/marketplace)                 | Browse spot capacity                                                                                    |
| [Billing](/api/billing)                         | Balance, costs, invoices/PDFs, ledger, saved cards, vouchers, node protection, auto-reload and payments |

[App configuration guide](/api/app-configuration) · [Response models](/api/models) · [Errors and recovery](/api/errors) · [Download OpenAPI](https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FgGXKmhKy3DIMEXqyyvgv%2Fkinesis-api-v1-openapi-1.5.0.json?alt=media)

For Kinesis compute, use `kinesis` in provider selectors such as `csp`. Discover supported regions and machine types through the [Catalog](/api/catalog).

## Choose the right endpoint

Use these public `/v1` endpoints for integrations. The portal also has browser-session routes for its own UI; those are separate from the bearer-authenticated public contract.

For agent integrations, [MCP](/mcp) exposes task-oriented tools over the same services. Compact mode separates read tools from `_write` tools. Credit purchases and auto-reload updates remain available through REST and the portal, but are no longer MCP actions. OAuth tokens from an MCP connection are valid for MCP only; use an API key for REST.


# API quickstart

Make your first request, create a project and launch a container.

Use the REST API at `https://api.kinesis.network/v1`. You need a Kinesis account and an API key from [Account settings](https://portal.kinesis.network/dashboard/account).

Open the **API keys** section in Account settings, create a key and copy its token when shown. Complete the two-factor setup or verification prompt if requested.

{% stepper %}
{% step %}

## Check your connection

The examples below use a POSIX shell. Set `KINESIS_API_KEY` in your shell or secret manager, then inspect the identity and permissions attached to it:

```bash
curl --fail-with-body https://api.kinesis.network/v1/me \
  -H "Authorization: Bearer $KINESIS_API_KEY"
```

Inspect `apiKey.scopes`, `apiKey.projectIds` and `limitedToOrgs`, then list reachable organizations:

```bash
curl --fail-with-body https://api.kinesis.network/v1/orgs \
  -H "Authorization: Bearer $KINESIS_API_KEY"
```

Choose the returned organization slug and set `KINESIS_ORG` to it. Your role there must be Admin or Developer to create a project.
{% endstep %}

{% step %}

## Create a project

Requires `projects:write`. A key restricted to specific projects cannot create a new project; use an unrestricted key or an existing permitted project.

```bash
curl --fail-with-body "https://api.kinesis.network/v1/projects?org=$KINESIS_ORG" \
  -H "Authorization: Bearer $KINESIS_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: quickstart-project-001' \
  -d '{"name":"API quickstart","visibility":"private"}'
```

Copy the returned `id` into `PROJECT_ID`. Use a new idempotency key for a different project; reuse this key and the same body only when retrying this request.
{% endstep %}

{% step %}

## Launch a container

Requires `apps:write` and `compute:spend`. Creating an app attempts to start it and can consume the organization’s credits. The project identifies the organization; no `org` parameter is needed for this resource call. This example uses serverless capacity: a Developer needs it enabled, or must add an accessible grid under `config.grids.ids`.

```bash
curl --fail-with-body "https://api.kinesis.network/v1/projects/$PROJECT_ID/apps" \
  -H "Authorization: Bearer $KINESIS_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: quickstart-app-001' \
  -d '{
    "name":"hello-kinesis",
    "config":{
      "image":"nginx:stable",
      "ports":[{"port":80,"protocol":"http80_load_balancer"}],
      "instances":{"min":1,"max":1}
    }
  }'
```

Copy the app's `id` into `APP_ID`. Inspect `start.state`: `requested`, `deferred`, `building` or `failed`. A created app is not necessarily ready to serve traffic.
{% endstep %}

{% step %}

## Follow the deployment

Requires `apps:read`. Poll status and use the addresses returned in `endpoints`:

```bash
curl --fail-with-body \
  "https://api.kinesis.network/v1/projects/$PROJECT_ID/apps/$APP_ID/status" \
  -H "Authorization: Bearer $KINESIS_API_KEY"
```

Read `state`, `targetState`, `nodes` and `errors`. An endpoint with a null address is not wired yet. For a repository deployment, poll the returned `buildId` first.
{% endstep %}

{% step %}

## Stop when you are finished

```bash
curl --fail-with-body -X POST \
  "https://api.kinesis.network/v1/projects/$PROJECT_ID/apps/$APP_ID/stop" \
  -H "Authorization: Bearer $KINESIS_API_KEY"
```

Poll status until stopped. App and project deletion require their apps to be stopped first. Dedicated-server billing is managed through the server lifecycle; stopping an app does not by itself prove that a dedicated server is hibernated.
{% endstep %}
{% endstepper %}

Next: [Authentication and scopes](/api/authentication), [request conventions](/api/conventions), or the [Apps reference](/api/apps).


# Authentication and scopes

API keys, organization selection, scopes and resource access.

REST requests use an API key in the HTTP header:

```http
Authorization: Bearer kn_your_api_key
```

Create and manage keys under **API keys** in [Account settings](https://portal.kinesis.network/dashboard/account). The token is shown once. Keep it in a secret manager and send it over HTTPS. Creating a key or changing its permissions may require your second factor; email/password accounts must enable two-factor authentication first. Browser sign-in cookies do not authenticate the public API.

## Whose permissions apply?

A key acts as its person in every organization they belong to, with their role in each. A Developer's key cannot administer infrastructure; a Viewer's key only reads. Scopes, organization limits, project limits and resource access all apply together. An `admin` scope never grants the Admin organization role.

Full access is `*`, including scopes added later and spending. Restricted keys carry explicit scopes, an optional expiry and a **Where it works** selection: all organizations, chosen organizations or specific projects. All-organizations keys include organizations the person joins later.

Use `GET /me` to inspect `apiKey.scopes`, `apiKey.projectIds` and top-level `limitedToOrgs`. `limitedToOrgs: null` means every organization the person belongs to; `GET /orgs` returns the organizations currently reachable and the person's role in each. An empty `projectIds` list means no project restriction; other restrictions still apply.

## Select an organization

Calls on a project, app or grid resolve the organization from the resource. Organization-level calls, such as creating a project or managing servers, billing or members, take the slug in `?org=`. It is a query parameter even for `POST` and `PATCH`.

```bash
curl --get https://api.kinesis.network/v1/members \
  -H "Authorization: Bearer $KINESIS_API_KEY" \
  --data-urlencode "org=$KINESIS_ORG"
```

Omit it only when the credential reaches one organization. With several, a call needing one organization fails with `org_required`; `error.details.orgs` lists the choices. `GET /projects` and `GET /apps` without `org` list across reachable organizations; supplying it narrows the list.

## Permission scopes

| Scope             | What it permits, subject to the person's role                            |
| ----------------- | ------------------------------------------------------------------------ |
| `projects:read`   | Read accessible projects                                                 |
| `projects:write`  | Create and update projects                                               |
| `projects:admin`  | Delete projects and manage project access                                |
| `apps:read`       | Read app configuration, status and builds                                |
| `apps:write`      | Create, update, start, stop, restart and rebuild apps                    |
| `apps:admin`      | Delete or move apps and manage app access                                |
| `pools:read`      | Read grids and servers                                                   |
| `pools:write`     | Manage grids, servers, provisioning, BYOD, Smart Lifecycle and SSH keys  |
| `pools:admin`     | Delete grids and manage grid access                                      |
| `monitoring:read` | Read usage, app logs, build logs and server logs                         |
| `vcs:read`        | Browse connected GitHub accounts and repositories                        |
| `billing:read`    | Read balance, costs, invoices, ledger and payment settings               |
| `billing:write`   | Change payment settings, redeem vouchers and create REST payments        |
| `members:read`    | Read members and pending organization invitations; see resource creators |
| `members:write`   | Invite/remove members, change roles and transfer ownership               |
| `org:write`       | Create organizations, rename them and change their settings              |
| `audit:read`      | Read the organization's audit log, including member identities           |
| `account:read`    | Read personal notifications and received invitations                     |
| `account:write`   | Manage notifications, answer invitations and leave shared resources      |
| `compute:spend`   | Authorize actions that start, resize or provision compute                |

Within a family, `admin` includes `write` and `read`, and `write` includes `read`. `compute:spend` is separate. So is the `audit` family: `org:write` does **not** grant `audit:read`. Grid scopes retain the name `pools`.

The reference lists exact scope combinations and input-dependent conditions. Creating, starting or restarting an app needs `apps:write` and `compute:spend`. Scopes authorize spending from the organization's balance within its role and compute controls.

## Resource access and creator identity

Projects and grids expose organization and visibility; projects, apps and grids include effective `access` (`role`, `outside`, `isCreator`) and `createdBy`. Apps follow their project. `createdBy` contains `userId`, `name` and `email`, or is null when the credential lacks `members:read` or the person is not a member of the resource's organization.

Admins manage every resource in their organization. Developers edit and operate apps they can access as Developer, and delete projects/apps they created. Admins or the creator manage project/app access; only admins manage grids and servers. Organization billing is admin-only.

Resource sharing uses `viewer` / `editor` in payloads; `editor` means Developer in the portal. Organization membership uses `viewer` / `developer` / `admin`. See [Sharing and visibility](/organizations/sharing).

## Restrictions on credentials

| Restriction                      | Effect                                                                                                                                |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Chosen organizations             | Acts only in those organizations while the person remains a member                                                                    |
| Specific projects                | Acts only on those projects and their apps; cannot create projects or run organization-wide infrastructure, billing or administration |
| Chosen organizations or projects | Cannot create an organization or accept/decline an invitation to join one                                                             |

Project-limited keys have narrow read exceptions: `get_org` reads the relevant organization, `list_members` supports sharing those projects, and costs require an explicit permitted `projectId` and an Admin organization role. Member activity/count details are omitted even for an admin using a project-limited key. These exceptions do not permit member administration or audit access. Disallowed operations return `key_project_restricted`; organization-limited operations outside their allowed scope are refused as `forbidden`.

Removing a member ends credential access in that organization. A credential limited to it loses it from its selection and is revoked if none remain. Resources and running apps remain with the organization.

## Single sign-on and credentials

Requiring organization SSO gates browser access; existing API keys and connected apps keep their access until membership, permissions, expiry or revocation changes. They do not require browser SSO renewal every 24 hours. Creating a key, changing its organization/project reach or approving a new connection checks the browser session’s access to each selected organization; complete its SSO when prompted. See [Single sign-on](/organizations/single-sign-on).

## OAuth and MCP

MCP connections can use OAuth or an API key. An OAuth connection acts as its person with approved scopes and can be limited to chosen organizations on the consent page. Its access token is valid for MCP only; use an API key for REST.

Connections approved before the organization scopes existed must be re-approved to use them. `insufficient_scope` names the required permissions; changing catalog mode or filters does not grant them. See [OAuth for MCP clients](/mcp/oauth).

For failures, follow [Errors and recovery](/api/errors). Include a request ID when available in support reports, never the credential itself.


# Request conventions

Request encoding, pagination, idempotency, rate limits and asynchronous operations.

## URLs and request bodies

All resource paths in the reference are relative to `https://api.kinesis.network/v1`. Download the [public OpenAPI specification](https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FgGXKmhKy3DIMEXqyyvgv%2Fkinesis-api-v1-openapi-1.5.0.json?alt=media) for the documented operations and provider choices. Use `csp=kinesis` for Kinesis compute.

`GET` and `DELETE` take non-path inputs in the query string. `POST` and `PATCH` take a JSON body. Organization selection is the exception: `org` stays in the query string for every HTTP method. Use `Content-Type: application/json` when sending a body. The body limit is 1 MiB.

Array query values can be repeated or comma-separated. Booleans accept `true`/`false` or `1`/`0`; numeric parameters are converted according to their schema. URL-encode path values, especially names and addresses containing reserved characters.

Most responses are JSON. `GET /billing/invoices/{invoiceNumber}/pdf` returns binary `application/pdf`; save it to a file rather than parsing JSON. By contrast, app log export returns JSON containing `text`, `lines` and `truncated`. The text export defaults to a 1 MiB budget and accepts at most 4 MiB; when truncated, newer lines may be missing.

| Identifier                  | Format                                     | Obtain it from               |
| --------------------------- | ------------------------------------------ | ---------------------------- |
| Organization                | Permanent URL slug                         | `GET /orgs`                  |
| Project or app              | 24-character hexadecimal ID                | Project/app listing          |
| Grid                        | GUID                                       | Grid listing                 |
| Server                      | `0x` followed by 40 hexadecimal characters | Server listing               |
| Build or provisioning order | Returned string ID                         | Creation response or listing |

## Organization selection and resource metadata

Use `?org=<slug>` on organization-level operations. Omitting it with several reachable organizations returns `org_required` and `error.details.orgs`. `GET /projects` and `GET /apps` instead span reachable organizations by default and accept `org` as a filter. Resource-ID calls on projects, apps and grids resolve their organization from that resource.

Projects and grids name their organization and visibility; projects, apps and grids carry `access` and `createdBy`. `access` describes effective `role`, `outside` and `isCreator`. Creator identities are null without `members:read` or when the caller is not a member of the resource’s organization. See [Authentication and scopes](/api/authentication).

## Server labels

Use the server response’s `name` for display and `address` for subsequent API calls. The caller’s own label takes precedence. A rented marketplace machine otherwise uses its hardware, Kinesis region and address suffix; its `hostname`, `csp`, `region`, `zone` and `instanceType` are null. `kcRegion` remains available for the Kinesis region.

The server’s `addedBy` field identifies who added it to the organization. It is null when nobody is recorded, for another organization’s server, or without `members:read`. It does not change server identifiers or ownership.

`GranteeList.ownerEmail` has been removed. Use the organization and member/access fields instead of depending on an individual resource owner.

## Pagination

For cursor-paginated lists, send `limit` and use the response's `nextCursor` in the next request. The usual default is 50 and maximum is 200; check the endpoint for exceptions. Stop only when `nextCursor` is null. Treat cursors as opaque and retain the same filters while paging.

```bash
curl --get https://api.kinesis.network/v1/projects \
  -H "Authorization: Bearer $KINESIS_API_KEY" \
  --data-urlencode 'limit=50' \
  --data-urlencode "cursor=$NEXT_CURSOR"
```

GitHub browsing, app logs and some catalog endpoints use different result shapes or pagination controls. Their schemas describe those explicitly.

## Accepted does not mean ready

App creation returns `201`; start, stop, restart and build triggers return `202`. A server provisioning dry run returns `200`, while a real order returns `202`. Inspect the response and poll the corresponding status resource.

```mermaid
flowchart LR
  A[Submit deployment] --> B{Source}
  B -->|Image| C[Read app status]
  B -->|GitHub| D[Poll build]
  D -->|Succeeded| C
  D -->|Failed| E[Read build error and logs]
  C --> F{Ready endpoint?}
  F -->|Yes| G[Use returned address]
  F -->|No| H[Inspect state and errors]
  H --> C
```

For builds and orders, follow `pollAfterSec` where it is returned. Avoid tight polling loops.

## Idempotency

These operations accept `Idempotency-Key` in the request header:

| Operation           | Endpoint                          |
| ------------------- | --------------------------------- |
| Create organization | `POST /orgs`                      |
| Create project      | `POST /projects`                  |
| Create app          | `POST /projects/{projectId}/apps` |
| Create grid         | `POST /grids`                     |
| Provision servers   | `POST /servers/provision`         |
| Replace server      | `POST /servers/{address}/replace` |

Use a unique string of 1–255 characters with no whitespace. Reuse it with the same credential, organization, operation and arguments after a timeout or lost response. A replay sets `Idempotent-Replayed: true`. Keys are retained for 24 hours.

`idempotency_conflict` means the same key was used with different arguments. `request_in_progress` means the original request is still running; wait for `retryAfterSec` and retry the same request. For server provisioning, use a different key when moving from a dry run to the actual order because the inputs changed.

{% hint style="warning" %}
Idempotency records the completed response. If a process fails after the backend acts but before that response is recorded, a retry can still repeat the action. Inspect the resource or order after an uncertain failure before issuing another create or provisioning request.
{% endhint %}

Saved-card top-ups use a required `idempotencyKey` in the JSON body, with their own payment-specific validation. The top-up reference is authoritative. Do not assume every write supports the header: restarting an app or triggering a build again performs another action.

## Rate limits and response headers

The default limits are 300 requests per minute per credential and 600 per user across credentials. The response headers describe the effective limit for your request.

| Header                  | Meaning                                  |
| ----------------------- | ---------------------------------------- |
| `X-Request-Id`          | Identifier for support and diagnostics   |
| `X-RateLimit-Limit`     | Effective request allowance              |
| `X-RateLimit-Remaining` | Requests remaining in the current window |
| `X-RateLimit-Reset`     | Window reset time, in Unix seconds       |
| `Retry-After`           | Seconds to wait when supplied            |
| `Idempotent-Replayed`   | True for an idempotent response replay   |

Rate limiting and authentication are shared by REST and MCP. For MCP, one HTTP request counts as one unit; JSON-RPC batches are rejected.

## Configuration updates

App `PATCH` requests do not use a universal deep-merge rule. Lists such as ports, environment variables and volumes replace the entire list. Hardware and placement objects merge by key; instance bounds must be supplied together; grid priorities replace their map. A null environment value preserves an existing variable; a new variable still needs a value. This also lets OpenAI-profile clients preserve credential-like values hidden from reads. Serialize app configuration changes when more than one client can write to the same app.


# Errors and recovery

Interpret errors, recover safely and provide useful diagnostics to support.

REST failures return a JSON error envelope. MCP tool failures put an error envelope in `structuredContent` and set `isError: true`; their request-ID behavior differs as described below. Authentication and rate limiting can fail before MCP runs a tool.

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "The credential lacks a required permission",
    "requestId": "<request-id>",
    "requiredScopes": ["apps:write"]
  }
}
```

This is an illustrative **REST** error. REST errors always include `requestId`. MCP tool errors include it only for server-side failures (status 500 or above); client errors such as `invalid_request`, `insufficient_scope` and `forbidden` omit it. The HTTP `X-Request-Id` header is unchanged, including failures before tool execution. Branch on `code`, and retain a request ID when one is present.

## Fix the request or access

| Code                     | Recovery                                                                              |
| ------------------------ | ------------------------------------------------------------------------------------- |
| `invalid_request`        | Inspect `issues` for field paths and validation messages                              |
| `payload_too_large`      | Reduce the request body below the reported byte limit                                 |
| `unauthorized`           | Check the bearer credential, expiry and revocation                                    |
| `insufficient_scope`     | Add the scopes listed in `requiredScopes`, or reauthorize MCP                         |
| `key_project_restricted` | Use a permitted project; project-limited keys cannot run organization-wide operations |
| `org_required`           | Pass an organization slug from `error.details.orgs` as REST `?org=` or MCP `org`      |
| `forbidden`              | The user's role cannot perform this action                                            |
| `not_found`              | Verify the identifier and the user's access to the resource                           |

In compact MCP mode, action argument errors use paths such as `params.projectId`. A failure against the action's input schema includes `action` and the full `inputSchema`; operation-level checks may return only the issues. Use `describe_action` to inspect selected actions before constructing complex arguments.

## Fix deployment state

| Code                                                     | Recovery                                                                                                     |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `app_running`                                            | Stop the app and wait before deleting                                                                        |
| `project_has_running_apps`                               | Stop the apps listed in `appIds` before deleting the project                                                 |
| `no_suitable_machines`                                   | Review hardware and placement; widen constraints where appropriate                                           |
| `grid_quota_exceeded`                                    | Free the creator’s quota, lower minimum instances or ask an organization admin                               |
| `serverless_disabled`                                    | Target an accessible grid or ask an organization admin; existing running apps are not stopped by the setting |
| `serverless_gpu_unavailable`                             | GPU apps require a grid; supply `config.grids.ids` or remove the GPU requirement                             |
| `build_in_progress`                                      | Wait for the active build to finish before rollback                                                          |
| `build_not_deployable`                                   | The app has never deployed or the build has no usable stored image; build the commit again                   |
| `grid_to_serverless`                                     | This conversion is unsupported; create a separate app if needed                                              |
| `app_source_unsupported`                                 | Manage this source through its supported portal flow                                                         |
| `app_config_unavailable`                                 | The `envFrom` source app has no configuration available to copy; inspect it before retrying                  |
| `app_not_on_grid`                                        | Confirm the runtime `appGuid` belongs to the grid before using grid app controls                             |
| `domain_not_verified`                                    | Create the returned DNS record and wait for propagation before retrying                                      |
| `github_installation_not_found`, `github_repo_not_found` | Check the connected GitHub installation and repository access                                                |
| `github_branch_not_found`, `github_commit_not_found`     | Browse the available branch or commit before retrying                                                        |
| `github_path_not_found`, `github_file_too_large`         | Check the repository path or select a smaller file for the contents API                                      |
| `github_not_configured`                                  | Repository deployment is not enabled on that deployment                                                      |
| `provision_order_failed`, `provision_quota`              | Inspect the order and the message before retrying or changing capacity                                       |

## Resolve account and payment issues

| Code                                                          | Recovery                                                                                                    |
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `insufficient_balance`, `balance_over_limit`                  | Inspect the balance and returned `billingUrl` or `topUpNeededCents`; `billingUrl` is an absolute portal URL |
| `no_payment_method`                                           | Add a payment method through the portal                                                                     |
| `card_declined`, `authentication_required`, `blocked_by_risk` | Resolve the payment issue in the portal; repeated charges are not a fix                                     |
| `amount_too_large`, `topup_limit`                             | Check the organization’s transaction cap or reset time                                                      |
| `app_creation_quota_exceeded`                                 | Check the organization’s app-creation allowance                                                             |
| `first_payment_in_dashboard`                                  | Complete the first payment through the portal before using saved-card top-ups                               |
| `voucher_not_found`, `voucher_rejected`                       | Check the code and the returned eligibility or redemption message                                           |
| `too_many_attempts`                                           | Pause repeated voucher attempts and follow the response before trying again                                 |
| `account_frozen`                                              | The person is frozen across organizations; contact support                                                  |
| `org_frozen`                                                  | The organization cannot spend or pay; an admin contacts support                                             |

REST may return `billingUrl` and payment diagnostics. MCP removes `billingUrl` and phrases balance failures as entitlement limits; explain the refusal and leave billing to an organization admin. MCP has no credit-purchase or auto-reload-setting action.

## Organization and member changes

| Code                            | Recovery                                                                   |
| ------------------------------- | -------------------------------------------------------------------------- |
| `owner`, `last_admin`           | Transfer ownership or retain another admin before changing membership      |
| `role_changed`, `owner_changed` | Read current members again before proposing another change                 |
| `invites_admins_only`           | An admin must invite, or enable developer invitations                      |
| `role_too_high`                 | Invite at your own organization role or below                              |
| `already_member`                | Add the existing member to the resource instead of inviting again          |
| `invitation_exists`             | An admin can resend another member’s pending invitation                    |
| `resend_too_soon`               | Wait at least a minute before resending to that address                    |
| `invitation_limit`              | Review pending invitations; the limit is 100                               |
| `invitation_closed`             | The invitation expired, was answered or was withdrawn; request a fresh one |
| `org_limit_reached`             | The person already owns five organizations                                 |
| `invalid_slug`, `slug_taken`    | Choose an available URL-safe slug                                          |

Organization- or project-limited credentials cannot create an organization or answer its invitations. Re-approve older OAuth connections for new scopes when needed. Old resource invitation cancellation differs from organization invitation revocation: cancelling an accepted resource invitation revokes its grant.

SSO errors such as `sso_required` during browser credential setup and `sso_session_needed` when enabling the requirement belong to portal sign-in/setup flows. They do not mean existing public API keys need a browser SSO session. See [SSO troubleshooting](/organizations/single-sign-on).

## Retry only when useful

| Code                                                        | Retry behavior                                                      |
| ----------------------------------------------------------- | ------------------------------------------------------------------- |
| `rate_limited`                                              | Wait for `Retry-After` or the reported reset time                   |
| `request_in_progress`                                       | Wait for `retryAfterSec`, then repeat with the same idempotency key |
| `idempotency_conflict`                                      | Do not replay changed arguments; use a new key for a new action     |
| `invalid_idempotency_key`                                   | Correct the key's length or format                                  |
| `topup_in_progress`                                         | Inspect the payment state and retain the payment's idempotency key  |
| `upstream_unavailable`, `upstream_github`, `internal_error` | Retry with backoff only when repeating the operation is safe        |

For creates and provisioning, follow the [idempotency rules](/api/conventions). For a restart, build trigger or other non-idempotent action, inspect current state before retrying after a lost response.

The endpoint reference lists additional operation-specific errors. If a problem persists, send [support](/support) the code, request ID when available, resource ID and approximate time, with credentials and secrets removed.


# App configuration

Understand app configuration, defaults, secrets, updates and deployment outcomes.

The Apps API accepts a `config` object for container runtime, network and resource requirements. For image apps, provide `config.image`. For repository apps, provide `source` and omit the image; the build determines it.

## Configuration groups

| Field                   | Purpose                                                   |
| ----------------------- | --------------------------------------------------------- |
| `image`, `registryAuth` | Image reference and optional private-registry credentials |
| `ports`                 | Container ports and exposure protocols                    |
| `env`                   | Environment variables and write-only secrets              |
| `command`, `entrypoint` | Container CMD and ENTRYPOINT overrides                    |
| `startupCommand`        | Command executed inside the container after startup       |
| `healthcheck`           | Check command and interval                                |
| `restartPolicy`         | Restart behavior, failure tolerance and task completion   |
| `instances`             | Minimum and maximum container count                       |
| `stateful`              | Pin an instance to its server and disk                    |
| `hardware`              | GPU requirements, RAM, disk and shared memory             |
| `placement`             | Allowed regions, providers and source IPs                 |
| `grids`                 | Grid IDs and priorities                                   |
| `volumes`               | Named persistent mounts for grid apps                     |

The [Apps reference](/api/apps) contains every nested field, type and supported enum. Runtime validation and capacity checks still apply after schema validation.

## Organization and compute permissions

Apps belong to their project’s organization and inherit its visibility. Developers can create and operate apps they can edit; only admins or an app’s creator can delete or move it. Grid targeting requires permission to deploy to the grid. Developers must target an accessible grid when `serverlessEnabled` is off. Grid quotas count live placements against the app’s creator; policy management is in the portal.

Serverless placement is CPU-only. A positive `hardware.gpuCount` requires at least one `grids.ids` entry. Older serverless GPU apps must move to a grid or remove the GPU requirement before their next configuration update; domain attachment/removal can encounter the same validation.

## Defaults and units

When omitted on creation, the current defaults are 1 GiB RAM, 1 GB disk, one to three instances, no GPU, any region and 64 MiB shared memory. Set instance bounds explicitly when a workload should use exactly one instance.

App hardware memory and storage fields use **bytes**. Dedicated-server provisioning uses `storageSizeGb`, while billing amounts use **US cents**. Do not interchange these units.

## Secrets

```json
{
  "env": [
    {"key": "MODEL_NAME", "value": "your-model"},
    {"key": "MODEL_TOKEN", "value": "<secret-value>", "secret": true}
  ]
}
```

Secret values are write-only and return null in reads. On update, null preserves the stored value of any existing variable. New variables need a value. Registry passwords are also write-only; reads expose the registry username separately.

## Update semantics

| Input                                       | Update behavior               |
| ------------------------------------------- | ----------------------------- |
| Omitted field                               | Keeps the stored setting      |
| Lists such as `env`, `ports`, `volumes`     | Replaces the entire list      |
| `hardware`, `placement`                     | Merges supplied keys          |
| `instances`                                 | Requires both `min` and `max` |
| `grids.priorities`                          | Replaces the priorities map   |
| `healthcheck: null`, `startupCommand: null` | Clears the setting            |
| Existing variable with `value: null`        | Preserves its stored value    |

Read configuration before editing arrays, and serialize concurrent writers. Changing compute sizing or build source can require `compute:spend`. Updating a running app may redeploy it.

The [OpenAI MCP profile](/mcp/openai-profile) does not accept registry credentials or secret environment values; set them in the portal. It also hides credential-like environment values on reads. Standard REST and MCP schemas below retain their supported credential fields.

## Repository source

```json
{
  "name": "service",
  "source": {
    "kind": "github",
    "installationId": 12345678,
    "repo": "your-org/your-repo",
    "branch": "main",
    "dockerfilePath": "Dockerfile",
    "contextDir": ".",
    "autoDeploy": true
  },
  "config": {
    "ports": [{"port": 3000, "protocol": "http80_load_balancer"}],
    "instances": {"min": 1, "max": 1}
  }
}
```

The identifiers above are placeholders. Discover the actual installation and repository with the [GitHub API](/api/github). An inline `dockerfileContent` can supply the Dockerfile. Updates to build source return the ID of the rebuild they trigger; poll the build and then app status.

## Additional creation options

### Copy environment variables from another app

On creation, use `envFrom` to copy variables and secret values on the server. The caller needs Developer access to the source app as well as permission to create in the destination project. This takes a copy once; future changes to the source app do not propagate.

```json
{
  "name": "web-copy",
  "config": {"image": "nginx:stable", "instances": {"min": 1, "max": 1}},
  "envFrom": {
    "projectId": "507f1f77bcf86cd799439011",
    "appId": "507f1f77bcf86cd799439012",
    "overrides": [{"key": "APP_ENV", "value": "staging"}]
  }
}
```

Replace the example IDs with the app to copy. Omit `config.env` when using `envFrom`; use `overrides` instead. Overrides match by key. A null value preserves the copied value, and a copied secret remains secret. If the source has no available configuration, the API returns `409 app_config_unavailable`.

### Clone a public GitHub repository

For a new repository-backed app, choose exactly one of `source.repo` or `source.clone`:

```json
{
  "name": "service-copy",
  "source": {
    "kind": "github",
    "installationId": 12345678,
    "clone": {
      "sourceRepo": "example-org/example-service",
      "targetRepoName": "my-service",
      "targetVisibility": "private"
    }
  },
  "config": {"instances": {"min": 1, "max": 1}}
}
```

Replace the installation and repository with real values. The source must be public. `sourceRepo` accepts `owner/name` or a GitHub URL, optionally with `/tree/<branch>`; `sourceRef` can select a branch, tag or commit. The destination is created in the connected account and defaults to private visibility. A member of the project’s organization with permission to edit it can make this request. Omit `config.image` and `config.registryAuth`; the build supplies the image.

Cloning runs in the build's `vcs_import` step. Follow the returned build ID, inspect errors or logs, and then poll app status. `browse_github` / the GitHub contents API can inspect source files; `source.dockerfileContent` supplies an inline Dockerfile if needed.

## Placement and project moves

Check placement before creating an app with `POST /projects/{projectId}/placement`, or check stored configuration and proposed changes with `POST /projects/{projectId}/apps/{appId}/placement`. These are read operations despite using POST and require `apps:read`. They report current capacity, hibernated capacity, provisioning possibilities and sampled rejection reasons; they do not reserve capacity.

`POST /projects/{projectId}/apps/{appId}/move` takes `targetProjectId` and requires `apps:admin`. Both projects must belong to the same organization. The caller must be an admin or the app’s creator and be allowed to edit the destination project. Use the returned destination project ID for later requests.

## Creation outcomes

| `start.state` | Meaning                                        | Next action                                                  |
| ------------- | ---------------------------------------------- | ------------------------------------------------------------ |
| `requested`   | Start accepted                                 | Poll app status                                              |
| `deferred`    | Capacity is being acquired first               | Inspect provisioning and poll status                         |
| `building`    | Repository image is being built                | Poll the returned build ID                                   |
| `failed`      | The app exists, but its automatic start failed | Read the error, correct the cause and start the existing app |

An HTTP success is not a readiness signal. Use the status resource and its returned endpoints to decide when traffic can reach the app.


# Account

Inspect the authenticated identity, permissions and account context.

API keys are personal and managed in Account settings. GET /me reports scopes, project restrictions and limitedToOrgs; GET /orgs lists reachable organizations with your role. Personal notifications remain attached to the person.

## Identify the caller: account, credential and permissions. The credential is an API key or a connected app

> Any API key can call this.

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Account"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Identity":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"}},"required":["id","email","name"],"additionalProperties":false},"limitedToOrgs":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"}},"required":["id","slug","name"],"additionalProperties":false}},{"type":"null"}],"description":"The organizations the key or connection is limited to; null when it reaches every one the user is in, including ones they join later (orgs.list has them, with the user's role in each)"},"apiKey":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"},"description":"Granted permissions; [\"*\"] is full access, including permissions added to the API later"},"projectIds":{"type":"array","items":{"type":"string"},"description":"Projects the key is limited to; empty means every project"}},"required":["id","name","scopes","projectIds"],"additionalProperties":false},{"type":"null"}],"description":"The key in use, or null when the caller is not using an API key"},"oauthClient":{"anyOf":[{"type":"object","properties":{"grantId":{"type":"string","description":"The connection as listed in the dashboard's connected apps"},"clientId":{"type":"string"},"clientName":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"},"description":"Permissions this connection was authorized with"}},"required":["grantId","clientId","clientName","scopes"],"additionalProperties":false},{"type":"null"}],"description":"The app connected through OAuth (hosted chat connectors), or null"}},"required":["user","limitedToOrgs","apiKey","oauthClient"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/me":{"get":{"operationId":"auth.whoami","summary":"Identify the caller: account, credential and permissions. The credential is an API key or a connected app","description":"Any API key can call this.","tags":["Account"],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Identity"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## The caller's active notifications, newest first, with the unseen count. Dismissed ones are left out

> Requires \`account:read\` (View invitations and notifications addressed to the user).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Account"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Notification":{"type":"object","properties":{"id":{"type":"string","description":"Notification id"},"type":{"type":"string","description":"What happened, e.g. share_invitation_received, grid_machines_online"},"title":{"type":"string"},"message":{"type":["string","null"]},"resourceType":{"anyOf":[{"type":"string","enum":["project","app","grid"]},{"type":"null"}]},"resourceId":{"type":["string","null"]},"url":{"description":"Dashboard page the notification points at","type":["string","null"]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"seenAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"resolved":{"type":"boolean","description":"Its inline action (accept, decline) has been taken already"}},"required":["id","type","title","message","resourceType","resourceId","url","createdAt","seenAt","resolved"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/notifications":{"get":{"operationId":"notifications.list","summary":"The caller's active notifications, newest first, with the unseen count. Dismissed ones are left out","description":"Requires `account:read` (View invitations and notifications addressed to the user).","tags":["Account"],"parameters":[{"name":"unseenOnly","in":"query","required":false,"description":"Only notifications not marked seen","schema":{"type":"boolean"}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"notifications":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}},"unseenCount":{"type":"integer","description":"Active notifications not marked seen, across every page"},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]}},"required":["notifications","unseenCount","nextCursor"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Dismiss one notification, or every active one. A dismissed notification no longer lists

> Requires \`account:write\` (Accept or decline invitations, leave shared resources, manage notifications).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Account"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/notifications":{"delete":{"operationId":"notifications.dismiss","summary":"Dismiss one notification, or every active one. A dismissed notification no longer lists","description":"Requires `account:write` (Accept or decline invitations, leave shared resources, manage notifications).","tags":["Account"],"parameters":[{"name":"id","in":"query","required":false,"description":"One notification; omit for every active one","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"dismissed":{"type":"integer","description":"Notifications dismissed"}},"required":["dismissed"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Mark one notification, or every active one, as seen

> Requires \`account:write\` (Accept or decline invitations, leave shared resources, manage notifications).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Account"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/notifications/seen":{"post":{"operationId":"notifications.markSeen","summary":"Mark one notification, or every active one, as seen","description":"Requires `account:write` (Accept or decline invitations, leave shared resources, manage notifications).","tags":["Account"],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"description":"One notification; omit for every active one","type":"string","pattern":"^[0-9a-fA-F]{24}$"}}}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"updated":{"type":"integer","description":"Notifications that changed"}},"required":["updated"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Organizations and members

Discover organizations with GET /orgs. Organization-level operations take the slug as ?org=, including POST and PATCH. Admins manage members, settings and audit; Developers invite at their role or below when enabled. Scopes never raise a role. Audit requires audit:read separately. Credentials limited to organizations or projects cannot create an organization or answer its invitations. Member details require an admin and a credential not limited to projects.

## The organizations the caller can act in, by name, with their role in each. Calls in an organization take its slug as org when there is more than one

> Any API key can call this.

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Organization":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","description":"What calls in an organization take as org"},"name":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role there, which bounds what they can do in it"}},"required":["id","slug","name","role"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/orgs":{"get":{"operationId":"orgs.list","summary":"The organizations the caller can act in, by name, with their role in each. Calls in an organization take its slug as org when there is more than one","description":"Any API key can call this.","tags":["Organizations"],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"orgs":{"type":"array","items":{"$ref":"#/components/schemas/Organization"}}},"required":["orgs"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Create an organization; the caller becomes its owner and admin. A person owns up to 5. A key or connection limited to some organizations or projects can't, since it would not reach the new one.

> Requires \`org:write\` (Create organizations, rename them and change their settings).\
> \
> Errors specific to this operation:\
> \- 400 \`invalid\_slug\`: The slug is reserved or not URL-safe\
> \- 409 \`slug\_taken\`: Another organization has that slug\
> \- 409 \`org\_limit\_reached\`: The caller already owns 5 organizations\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"IdempotentReplayed":{"description":"true when the response was replayed for a reused Idempotency-Key rather than produced anew","schema":{"type":"boolean"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"OrganizationDetail":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","description":"What calls in an organization take as org"},"name":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role there, which bounds what they can do in it"},"isOwner":{"type":"boolean","description":"The caller owns it (members.list names the owner)"},"settings":{"type":"object","properties":{"serverlessEnabled":{"type":"boolean","description":"Developers may run apps on Kinesis serverless capacity, billed to the organization; admins always can"},"membersCanInvite":{"type":"boolean","description":"Developers may invite people, as developers or viewers; admins always can"}},"required":["serverlessEnabled","membersCanInvite"],"additionalProperties":false},"frozen":{"type":"boolean","description":"Frozen over a payment problem: it can't start apps, add servers or pay"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","slug","name","role","isOwner","settings","frozen","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/orgs":{"post":{"operationId":"orgs.create","summary":"Create an organization; the caller becomes its owner and admin. A person owns up to 5. A key or connection limited to some organizations or projects can't, since it would not reach the new one.","description":"Requires `org:write` (Create organizations, rename them and change their settings).\n\nErrors specific to this operation:\n- 400 `invalid_slug`: The slug is reserved or not URL-safe\n- 409 `slug_taken`: Another organization has that slug\n- 409 `org_limit_reached`: The caller already owns 5 organizations\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string of at most 255 characters. A retry with the same key and body returns the first call's response (with Idempotent-Replayed: true) instead of acting twice; a different body is refused with 409 idempotency_conflict, and a retry while the first call is still running gets 409 request_in_progress. Keys are kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^\\S+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"The organization's name"},"slug":{"description":"Its name in URLs and as org; lowercase letters, digits and dashes. Made from the name when omitted","type":"string","minLength":3,"maxLength":39}},"required":["name"]}}}},"responses":{"201":{"description":"Created","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDetail"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Open invitations to join an organization, addressed to the caller's email

> Requires \`account:read\` (View invitations and notifications addressed to the user).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"OrgInvitation":{"type":"object","properties":{"id":{"type":"string","description":"Invitation id"},"org":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The role the caller would join with"},"invitedBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}]},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","org","role","invitedBy","expiresAt"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/orgs/invitations":{"get":{"operationId":"orgs.invitations.received","summary":"Open invitations to join an organization, addressed to the caller's email","description":"Requires `account:read` (View invitations and notifications addressed to the user).","tags":["Organizations"],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"invitations":{"type":"array","items":{"$ref":"#/components/schemas/OrgInvitation"}}},"required":["invitations"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Accept an invitation to an organization. The caller joins with the invited role, and is added to what the invitation named. Not for a key or connection limited to some organizations or projects.

> Requires \`account:write\` (Accept or decline invitations, leave shared resources, manage notifications).\
> \
> Errors specific to this operation:\
> \- 409 \`invitation\_closed\`: The invitation was answered, withdrawn or has expired\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Organization":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","description":"What calls in an organization take as org"},"name":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role there, which bounds what they can do in it"}},"required":["id","slug","name","role"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/orgs/invitations/{invitationId}/accept":{"post":{"operationId":"orgs.invitations.accept","summary":"Accept an invitation to an organization. The caller joins with the invited role, and is added to what the invitation named. Not for a key or connection limited to some organizations or projects.","description":"Requires `account:write` (Accept or decline invitations, leave shared resources, manage notifications).\n\nErrors specific to this operation:\n- 409 `invitation_closed`: The invitation was answered, withdrawn or has expired\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"invitationId","in":"path","required":true,"description":"Invitation id, from orgs.invitations.received","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"org":{"$ref":"#/components/schemas/Organization"},"joined":{"type":"boolean","description":"False when the caller was a member already and keeps their role"}},"required":["org","joined"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Decline an invitation to an organization. Its link stops working

> Requires \`account:write\` (Accept or decline invitations, leave shared resources, manage notifications).\
> \
> Errors specific to this operation:\
> \- 409 \`invitation\_closed\`: The invitation was answered, withdrawn or has expired\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/orgs/invitations/{invitationId}/decline":{"post":{"operationId":"orgs.invitations.decline","summary":"Decline an invitation to an organization. Its link stops working","description":"Requires `account:write` (Accept or decline invitations, leave shared resources, manage notifications).\n\nErrors specific to this operation:\n- 409 `invitation_closed`: The invitation was answered, withdrawn or has expired\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"invitationId","in":"path","required":true,"description":"Invitation id, from orgs.invitations.received","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"declined":{"type":"boolean","const":true}},"required":["declined"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## An organization's settings, whether it is frozen, and the caller's role in it

> Any API key can call this.\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"OrganizationDetail":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","description":"What calls in an organization take as org"},"name":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role there, which bounds what they can do in it"},"isOwner":{"type":"boolean","description":"The caller owns it (members.list names the owner)"},"settings":{"type":"object","properties":{"serverlessEnabled":{"type":"boolean","description":"Developers may run apps on Kinesis serverless capacity, billed to the organization; admins always can"},"membersCanInvite":{"type":"boolean","description":"Developers may invite people, as developers or viewers; admins always can"}},"required":["serverlessEnabled","membersCanInvite"],"additionalProperties":false},"frozen":{"type":"boolean","description":"Frozen over a payment problem: it can't start apps, add servers or pay"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","slug","name","role","isOwner","settings","frozen","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/org":{"get":{"operationId":"orgs.get","summary":"An organization's settings, whether it is frozen, and the caller's role in it","description":"Any API key can call this.\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs","tags":["Organizations"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDetail"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Rename an organization or change its settings (admins). Its slug never changes

> Requires \`org:write\` (Create organizations, rename them and change their settings).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"OrganizationDetail":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","description":"What calls in an organization take as org"},"name":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role there, which bounds what they can do in it"},"isOwner":{"type":"boolean","description":"The caller owns it (members.list names the owner)"},"settings":{"type":"object","properties":{"serverlessEnabled":{"type":"boolean","description":"Developers may run apps on Kinesis serverless capacity, billed to the organization; admins always can"},"membersCanInvite":{"type":"boolean","description":"Developers may invite people, as developers or viewers; admins always can"}},"required":["serverlessEnabled","membersCanInvite"],"additionalProperties":false},"frozen":{"type":"boolean","description":"Frozen over a payment problem: it can't start apps, add servers or pay"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","slug","name","role","isOwner","settings","frozen","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/org":{"patch":{"operationId":"orgs.update","summary":"Rename an organization or change its settings (admins). Its slug never changes","description":"Requires `org:write` (Create organizations, rename them and change their settings).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"The organization's name"},"serverlessEnabled":{"type":"boolean","description":"Developers may run apps on Kinesis serverless capacity, billed to the organization; admins always can"},"membersCanInvite":{"type":"boolean","description":"Developers may invite people, as developers or viewers; admins always can"}}}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationDetail"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## The organization's audit log, latest recorded first (admins). Who did what to its projects, apps, grids, servers, members and settings, kept for a year

> Requires \`audit:read\` (Read the organization's audit log: who did what, with members' names and emails).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"AuditEntry":{"type":"object","properties":{"id":{"type":"string"},"at":{"type":"string","format":"date-time","description":"When it happened"},"actor":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who did it; null for the platform, or an account that is gone"},"viaSupport":{"type":"boolean","description":"Kinesis support did it while signed in as the actor"},"role":{"description":"The actor's role at the time","type":["string","null"]},"resourceType":{"type":"string","description":"project, app, pool (a grid) or org (its members, settings and servers)"},"resourceId":{"description":"Project or app id, grid id, or the organization's id","type":["string","null"]},"resourceName":{"description":"The project's or app's name, while it exists","type":["string","null"]},"action":{"type":"string","description":"What was done, e.g. appCreate, appStop, memberInvite, serverProvision"},"outcome":{"type":"string","enum":["success","error"]},"errorCode":{"description":"Why it failed, as a code; the stored message can name internals","type":["string","null"]},"details":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"What the action concerned: an app's name, a member's email and role, server addresses, ..."},"changes":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The field, e.g. name or envVars.API_KEY"},"kind":{"type":"string","enum":["added","removed","changed"]},"before":{},"after":{},"redacted":{"description":"A secret: its values are not kept","type":"boolean"}},"required":["path","kind"],"additionalProperties":false},"description":"Field-level changes, for actions that edit fields"},"message":{"description":"For something the platform did: what it reported","type":["string","null"]}},"required":["id","at","actor","viaSupport","role","resourceType","resourceId","resourceName","action","outcome","errorCode","details","changes","message"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/org/audit":{"get":{"operationId":"orgs.audit.list","summary":"The organization's audit log, latest recorded first (admins). Who did what to its projects, apps, grids, servers, members and settings, kept for a year","description":"Requires `audit:read` (Read the organization's audit log: who did what, with members' names and emails).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/AuditEntry"}},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]}},"required":["entries","nextCursor"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## The members of an organization and their roles, admins first. These are the people add\_grantees can add to its projects, apps and grids

> Requires \`members:read\` (View the organization's members, their roles and pending invitations).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Member":{"type":"object","properties":{"userId":{"type":"string","description":"What add_grantees and the member calls take"},"email":{"type":"string"},"name":{"type":["string","null"]},"role":{"type":"string","enum":["admin","developer","viewer"]},"joinedAt":{"type":"string"},"isOwner":{"type":"boolean","description":"Owns the organization: always an admin, and stays until they transfer ownership"},"lastSignInAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"With details, for admins: when they last signed in"},"created":{"anyOf":[{"type":"object","properties":{"projects":{"type":"integer"},"apps":{"type":"integer"},"grids":{"type":"integer"}},"required":["projects","apps","grids"],"additionalProperties":false},{"type":"null"}],"description":"With details, for admins: what they created here, which stays when they leave"}},"required":["userId","email","name","role","joinedAt","isOwner","lastSignInAt","created"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/members":{"get":{"operationId":"members.list","summary":"The members of an organization and their roles, admins first. These are the people add_grantees can add to its projects, apps and grids","description":"Requires `members:read` (View the organization's members, their roles and pending invitations).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs","tags":["Organizations"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"details","in":"query","required":false,"description":"For admins: also each member's last sign-in and what they created (lastSignInAt, created)","schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/Member"}}},"required":["members"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Hand the organization to another member (its owner only). They become an admin if they aren't one; the previous owner stays an admin.

> Requires \`members:write\` (Invite and remove members, change their roles, transfer ownership).\
> \
> Errors specific to this operation:\
> \- 400 \`already\_owner\`: The caller named themselves\
> \- 409 \`owner\_changed\`: Ownership moved while this ran; read the members again\
> \- 409 \`role\_changed\`: Their role changed, or they left, while this ran; read the members again\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/members/owner":{"post":{"operationId":"members.owner.transfer","summary":"Hand the organization to another member (its owner only). They become an admin if they aren't one; the previous owner stays an admin.","description":"Requires `members:write` (Invite and remove members, change their roles, transfer ownership).\n\nErrors specific to this operation:\n- 400 `already_owner`: The caller named themselves\n- 409 `owner_changed`: Ownership moved while this ran; read the members again\n- 409 `role_changed`: Their role changed, or they left, while this ran; read the members again\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string","pattern":"^[0-9a-fA-F]{24}$","description":"The member's user id, from members.list"}},"required":["userId"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"ownerUserId":{"type":"string","description":"The new owner"}},"required":["ownerUserId"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Pending invitations to the organization, newest first. Admins see every one; a developer the ones they sent

> Requires \`members:read\` (View the organization's members, their roles and pending invitations).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"MemberInvitation":{"type":"object","properties":{"id":{"type":"string","description":"Invitation id"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The role they join with"},"invitedBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}]},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"expired":{"type":"boolean","description":"Past its date; inviting the address again sends a fresh link"},"addsTo":{"type":"array","items":{"type":"string"},"description":"Names of the projects, apps and grids they are added to on joining"}},"required":["id","email","role","invitedBy","expiresAt","expired","addsTo"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/members/invitations":{"get":{"operationId":"members.invitations.list","summary":"Pending invitations to the organization, newest first. Admins see every one; a developer the ones they sent","description":"Requires `members:read` (View the organization's members, their roles and pending invitations).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"invitations":{"type":"array","items":{"$ref":"#/components/schemas/MemberInvitation"}}},"required":["invitations"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Invite someone to the organization by email, at the caller's role or below. They get a link good for 7 days; inviting an address again sends a fresh one. addTo also adds them to a project, app or grid on joining.

> Requires \`members:write\` (Invite and remove members, change their roles, transfer ownership).\
> \
> Also requires \`projects:admin\` (Delete projects and manage who they are shared with) when addTo.resourceType is project.\
> \
> Also requires \`apps:admin\` (Delete or move apps and manage who they are shared with) when addTo.resourceType is app.\
> \
> Also requires \`pools:admin\` (Delete grids and manage who they are shared with) when addTo.resourceType is grid.\
> \
> Errors specific to this operation:\
> \- 400 \`invalid\_email\`: Not an email address\
> \- 403 \`invites\_admins\_only\`: The organization keeps inviting to its admins\
> \- 403 \`role\_too\_high\`: The role is above the caller's own\
> \- 409 \`already\_member\`: They are in the organization already\
> \- 409 \`invitation\_exists\`: Someone else invited them; an admin can send it again\
> \- 429 \`resend\_too\_soon\`: An invitation went to this address under a minute ago\
> \- 429 \`invitation\_limit\`: 100 invitations are pending; revoke some first\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/members/invitations":{"post":{"operationId":"members.invitations.create","summary":"Invite someone to the organization by email, at the caller's role or below. They get a link good for 7 days; inviting an address again sends a fresh one. addTo also adds them to a project, app or grid on joining.","description":"Requires `members:write` (Invite and remove members, change their roles, transfer ownership).\n\nAlso requires `projects:admin` (Delete projects and manage who they are shared with) when addTo.resourceType is project.\n\nAlso requires `apps:admin` (Delete or move apps and manage who they are shared with) when addTo.resourceType is app.\n\nAlso requires `pools:admin` (Delete grids and manage who they are shared with) when addTo.resourceType is grid.\n\nErrors specific to this operation:\n- 400 `invalid_email`: Not an email address\n- 403 `invites_admins_only`: The organization keeps inviting to its admins\n- 403 `role_too_high`: The role is above the caller's own\n- 409 `already_member`: They are in the organization already\n- 409 `invitation_exists`: Someone else invited them; an admin can send it again\n- 429 `resend_too_soon`: An invitation went to this address under a minute ago\n- 429 `invitation_limit`: 100 invitations are pending; revoke some first\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","minLength":1,"maxLength":320,"description":"Who to invite"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The role they join with: admin, developer or viewer"},"addTo":{"description":"A project, app or grid of the organization the caller may add people to","type":"object","properties":{"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string","minLength":1,"description":"Project or app id, or grid id"}},"required":["resourceType","resourceId"]}},"required":["email","role"]}}}},"responses":{"201":{"description":"Created","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Invitation id"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The role they join with"},"invitedBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}]},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"expired":{"type":"boolean","description":"Past its date; inviting the address again sends a fresh link"},"addsTo":{"type":"array","items":{"type":"string"},"description":"Names of the projects, apps and grids they are added to on joining"},"warning":{"description":"Set when the invitation was saved but its email could not be sent","type":["string","null"]}},"required":["id","email","role","invitedBy","expiresAt","expired","addsTo","warning"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Withdraw a pending invitation to the organization. Its link stops working

> Requires \`members:write\` (Invite and remove members, change their roles, transfer ownership).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/members/invitations/{invitationId}":{"delete":{"operationId":"members.invitations.revoke","summary":"Withdraw a pending invitation to the organization. Its link stops working","description":"Requires `members:write` (Invite and remove members, change their roles, transfer ownership).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"invitationId","in":"path","required":true,"description":"Invitation id, from members.invitations.list","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"invitationId":{"type":"string"},"revoked":{"type":"boolean","const":true}},"required":["invitationId","revoked"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Change a member's role (admins). Their invitations above the new role are withdrawn

> Requires \`members:write\` (Invite and remove members, change their roles, transfer ownership).\
> \
> Errors specific to this operation:\
> \- 409 \`last\_admin\`: An organization keeps at least one admin\
> \- 409 \`owner\`: The owner can't leave, be removed or change role until they transfer ownership\
> \- 409 \`role\_changed\`: Their role changed, or they left, while this ran; read the members again\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/members/{userId}":{"patch":{"operationId":"members.update","summary":"Change a member's role (admins). Their invitations above the new role are withdrawn","description":"Requires `members:write` (Invite and remove members, change their roles, transfer ownership).\n\nErrors specific to this operation:\n- 409 `last_admin`: An organization keeps at least one admin\n- 409 `owner`: The owner can't leave, be removed or change role until they transfer ownership\n- 409 `role_changed`: Their role changed, or they left, while this ran; read the members again\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"userId","in":"path","required":true,"description":"The member's user id, from members.list","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"Their new role"}},"required":["role"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"]}},"required":["userId","role"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Remove a member (admins), or leave with the caller's own user id. Their access, keys and connected apps in the organization end; what they created stays and their apps keep running.

> Requires \`members:write\` (Invite and remove members, change their roles, transfer ownership).\
> \
> Errors specific to this operation:\
> \- 409 \`last\_admin\`: An organization keeps at least one admin\
> \- 409 \`owner\`: The owner can't leave, be removed or change role until they transfer ownership\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Organizations"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/members/{userId}":{"delete":{"operationId":"members.remove","summary":"Remove a member (admins), or leave with the caller's own user id. Their access, keys and connected apps in the organization end; what they created stays and their apps keep running.","description":"Requires `members:write` (Invite and remove members, change their roles, transfer ownership).\n\nErrors specific to this operation:\n- 409 `last_admin`: An organization keeps at least one admin\n- 409 `owner`: The owner can't leave, be removed or change role until they transfer ownership\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Organizations"],"parameters":[{"name":"userId","in":"path","required":true,"description":"The member's user id, from members.list","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string"},"removed":{"type":"boolean","const":true},"warning":{"description":"Set when a grid they had contributed to could not be fully released","type":["string","null"]}},"required":["userId","removed","warning"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Projects

Create and organize projects, and manage their lifecycle.

Projects belong to organizations and are private by default. Supply ?org= when creating in an organization; list calls can filter by org or span all reachable organizations. Admins or the creator manage visibility and deletion. A Developer deleting a project must also have created every app inside it. Stop all apps before API deletion.

## List projects accessible to the caller, oldest first, across reachable organizations unless org narrows the list

> Requires \`projects:read\` (View projects).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Projects"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Project":{"type":"object","properties":{"id":{"type":"string","description":"Project id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"access":{"$ref":"#/components/schemas/Access"},"appCount":{"type":"integer"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","name","org","visibility","access","appCount","createdBy","createdAt"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects":{"get":{"operationId":"projects.list","summary":"List projects accessible to the caller, oldest first, across reachable organizations unless org narrows the list","description":"Requires `projects:read` (View projects).","tags":["Projects"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of an organization (see orgs.list) to list only what is in it or shared into it","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"projects":{"type":"array","items":{"$ref":"#/components/schemas/Project"}},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]}},"required":["projects","nextCursor"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Create an empty project. It is private unless visibility is org

> Requires \`projects:write\` (Create and rename projects).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Projects"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"IdempotentReplayed":{"description":"true when the response was replayed for a reused Idempotency-Key rather than produced anew","schema":{"type":"boolean"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Project":{"type":"object","properties":{"id":{"type":"string","description":"Project id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"access":{"$ref":"#/components/schemas/Access"},"appCount":{"type":"integer"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","name","org","visibility","access","appCount","createdBy","createdAt"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects":{"post":{"operationId":"projects.create","summary":"Create an empty project. It is private unless visibility is org","description":"Requires `projects:write` (Create and rename projects).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Projects"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string of at most 255 characters. A retry with the same key and body returns the first call's response (with Idempotent-Replayed: true) instead of acting twice; a different body is refused with 409 idempotency_conflict, and a retry while the first call is still running gets 409 request_in_progress. Keys are kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^\\S+$"}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Project name"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"}},"required":["name"]}}}},"responses":{"201":{"description":"Created","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Get a project and the apps in it

> Requires \`projects:read\` (View projects).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Projects"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ProjectDetail":{"type":"object","properties":{"id":{"type":"string","description":"Project id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"access":{"$ref":"#/components/schemas/Access"},"appCount":{"type":"integer"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"apps":{"type":"array","items":{"$ref":"#/components/schemas/App"}}},"required":["id","name","org","visibility","access","appCount","createdBy","createdAt","apps"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"App":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}":{"get":{"operationId":"projects.get","summary":"Get a project and the apps in it","description":"Requires `projects:read` (View projects).","tags":["Projects"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectDetail"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Rename a project, or change who in its organization can see it. Visibility is for its creator or an admin: private is admins, its creator and the people added; org is everyone in the organization

> Requires \`projects:write\` (Create and rename projects).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Projects"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Project":{"type":"object","properties":{"id":{"type":"string","description":"Project id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"access":{"$ref":"#/components/schemas/Access"},"appCount":{"type":"integer"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","name","org","visibility","access","appCount","createdBy","createdAt"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}":{"patch":{"operationId":"projects.update","summary":"Rename a project, or change who in its organization can see it. Visibility is for its creator or an admin: private is admins, its creator and the people added; org is everyone in the organization","description":"Requires `projects:write` (Create and rename projects).","tags":["Projects"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"New project name","type":"string","minLength":1,"maxLength":100},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"}}}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Delete a project and every app in it. Admins may, and so may its creator when they created every app in it. Fails with project\_has\_running\_apps while any app is live. Irreversible.

> Requires \`projects:admin\` (Delete projects and manage who they are shared with).\
> \
> Errors specific to this operation:\
> \- 409 \`project\_has\_running\_apps\`: An app in the project is still live; appIds lists them. Stop them and retry once they report stopped

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Projects"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}":{"delete":{"operationId":"projects.delete","summary":"Delete a project and every app in it. Admins may, and so may its creator when they created every app in it. Fails with project_has_running_apps while any app is live. Irreversible.","description":"Requires `projects:admin` (Delete projects and manage who they are shared with).\n\nErrors specific to this operation:\n- 409 `project_has_running_apps`: An app in the project is still live; appIds lists them. Stop them and retry once they report stopped","tags":["Projects"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Id of the deleted project"},"deleted":{"type":"boolean","const":true}},"required":["id","deleted"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Apps

Deploy, configure and operate container applications.

Serverless placement is CPU-only. A GPU app must name at least one config.grids.ids entry; otherwise create/update can return serverless\_gpu\_unavailable. Log and export endpoints return raw application output and may include sensitive data.

Apps follow their project’s organization and visibility. Resource paths identify the organization; GET /apps can filter by org. Developers operate apps they can edit. Admins or the app’s creator delete or move it; moves stay within an organization. Grid quotas count against the app’s creator, and serverless settings still apply.

## Check which servers could run an app that does not exist yet, from the placement-relevant parts of its configuration, before create\_app. Nothing is created.

> Requires \`apps:read\` (View apps, their configuration and status).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Placement":{"type":"object","properties":{"placeable":{"type":"boolean","description":"A server can take the app now, a hibernated one would fit once woken, or one can be provisioned"},"serversWithCapacity":{"type":"integer","description":"Servers that match and have room now"},"serversWithoutCapacity":{"type":"integer","description":"Servers that match but are full"},"wakeable":{"type":"integer","description":"Hibernated servers that would match once woken (power_servers wake, or the automatic wake-up on start)"},"wakeableAwaitingStock":{"type":"integer","description":"Of wakeable, servers whose provider has no stock right now, so a wake would not land yet"},"provisionable":{"type":"boolean","description":"The platform can provision a matching cloud machine on demand for a serverless app"},"rejections":{"type":"array","items":{"type":"object","properties":{"server":{"type":"string","description":"Server address"},"reason":{"type":"string"}},"required":["server","reason"],"additionalProperties":false},"description":"Why sampled servers cannot take the app; each distinct reason appears first, then repeats"}},"required":["placeable","serversWithCapacity","serversWithoutCapacity","wakeable","wakeableAwaitingStock","provisionable","rejections"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/placement":{"post":{"operationId":"projects.placement","summary":"Check which servers could run an app that does not exist yet, from the placement-relevant parts of its configuration, before create_app. Nothing is created.","description":"Requires `apps:read` (View apps, their configuration and status).","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project the app would be created in","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"config":{"type":"object","properties":{"instances":{"description":"Instance count range","type":"object","properties":{"min":{"type":"integer","minimum":0,"description":"Instances to keep running"},"max":{"type":"integer","minimum":0,"description":"Upper bound for scaling"}},"required":["min","max"],"additionalProperties":false},"stateful":{"description":"Keep each instance pinned to its server and disk","type":"boolean"},"hardware":{"description":"Hardware requirements per instance","type":"object","properties":{"gpuCount":{"description":"GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only","type":"integer","minimum":0,"maximum":16},"gpuModels":{"description":"Acceptable GPU models, e.g. H100; empty means any","maxItems":16,"type":"array","items":{"type":"string","minLength":1}},"gpuManufacturer":{"description":"GPU vendor; null means any","anyOf":[{"type":"string","enum":["amd","apple","intel","nvidia","xilinx"]},{"type":"null"}]},"gpuVramBytes":{"description":"Minimum VRAM per GPU, in bytes","type":"integer","minimum":0},"ramBytes":{"description":"RAM per instance, in bytes","type":"integer","exclusiveMinimum":0},"storageBytes":{"description":"Disk per instance, in bytes","type":"integer","exclusiveMinimum":0},"shmemBytes":{"description":"Shared memory (/dev/shm) size, in bytes","type":"integer","minimum":0}},"additionalProperties":false},"placement":{"description":"Where the app may run and who may reach it","type":"object","properties":{"regions":{"description":"Kinesis regions to run in; empty means any","type":"array","items":{"type":"string","enum":["us-west","us-east","us-central","ca-west","ca-east","ca-central","eu-west","eu-north","eu-east","eu-south","eu-central","eu-south-east","ap-east","ap-south-east","ap-north-east","ap-south"]}},"providers":{"description":"Restrict placement to specific providers and regions","type":"array","items":{"type":"object","properties":{"csp":{"type":"string","enum":["aws","azure","gcp","kinesis"],"description":"Cloud provider"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string","minLength":1,"description":"Provider region"},"zones":{"type":"array","items":{"type":"string"},"description":"Zones within the region; empty means any"}},"required":["region","zones"],"additionalProperties":false},"description":"Regions of the provider to allow"}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"description":"Source IPs or CIDR ranges allowed to reach the app; empty means everyone","maxItems":100,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"grids":{"description":"Run on grids instead of serverless capacity (members only, on grids they may deploy to)","type":"object","properties":{"ids":{"description":"Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"priorities":{"description":"Priority per grid id, -100 to 100","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"integer","minimum":-100,"maximum":100}}},"additionalProperties":false},"volumes":{"description":"Persistent volumes","maxItems":4,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$","description":"Volume name, unique within the app"},"target":{"type":"string","pattern":"^\\/.+","description":"Absolute mount path inside the container"},"declaredSizeBytes":{"description":"Expected size, used for placement; 0 or omitted means unspecified","type":"integer","minimum":0,"maximum":17592186044416}},"required":["name","target"],"additionalProperties":false}}},"additionalProperties":false,"description":"The placement-relevant parts of an app configuration: instances, stateful, hardware, placement, grids, volumes"}},"required":["config"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Placement"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## List the apps the caller can see, oldest first. Across every project, or within one

> Requires \`apps:read\` (View apps, their configuration and status).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"App":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt"],"additionalProperties":false},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/apps":{"get":{"operationId":"apps.list","summary":"List the apps the caller can see, oldest first. Across every project, or within one","description":"Requires `apps:read` (View apps, their configuration and status).","tags":["Apps"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of an organization (see orgs.list) to list only what is in it or shared into it","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"projectId","in":"query","required":false,"description":"Limit the list to one project","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"apps":{"type":"array","items":{"$ref":"#/components/schemas/App"}},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]}},"required":["apps","nextCursor"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## List the apps in a project

> Requires \`apps:read\` (View apps, their configuration and status).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"App":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt"],"additionalProperties":false},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps":{"get":{"operationId":"projects.apps","summary":"List the apps in a project","description":"Requires `apps:read` (View apps, their configuration and status).","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Limit the list to one project","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"org","in":"query","required":false,"description":"Slug of an organization (see orgs.list) to list only what is in it or shared into it","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"apps":{"type":"array","items":{"$ref":"#/components/schemas/App"}},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]}},"required":["apps","nextCursor"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Create an app from a container image, or from a GitHub repository with source, and start it. With envFrom the new app takes another app's environment variables, secret values included, copied on the server. Omitted config fields take the dashboard defaults (1 GiB RAM, 1 GB disk, 1 to 3 instances, no GPU).

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps), \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\
> \
> Errors specific to this operation:\
> \- 402 \`balance\_over\_limit\`: The organization's balance is below the allowed negative limit; add credits and retry\
> \- 429 \`app\_creation\_quota\_exceeded\`: The organization's app creation quota is used up; add a payment method or wait for the window\
> \- 400 \`no\_suitable\_machines\`: No machine can run this configuration; lower the hardware requirements or ask for provisioning\
> \- 400 \`serverless\_gpu\_unavailable\`: The app asks for a GPU and targets no grid; serverless placement is CPU-only, so pass grids.ids\
> \- 429 \`grid\_quota\_exceeded\`: The grids the app targets have no room for it under their per-person app and instance quotas\
> \- 403 \`serverless\_disabled\`: The app would run on Kinesis serverless capacity, which the organization keeps to its admins; target a grid\
> \- 503 \`github\_not\_configured\`: GitHub deployments are not enabled on this server\
> \- 404 \`github\_installation\_not\_found\`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\
> \- 404 \`github\_repo\_not\_found\`: The installation cannot see the repository; grant the GitHub App access to it\
> \- 502 \`upstream\_github\`: GitHub did not answer; retry shortly\
> \- 404 \`github\_branch\_not\_found\`: The repository has no such branch\
> \- 409 \`app\_config\_unavailable\`: The envFrom app has no configuration to copy from

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"IdempotentReplayed":{"description":"true when the response was replayed for a reused Idempotency-Key rather than produced anew","schema":{"type":"boolean"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"AppCreateResult":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"status":{"description":"Status as last cached; use the status endpoint for a live read","$ref":"#/components/schemas/AppStatus"},"config":{"anyOf":[{"$ref":"#/components/schemas/AppConfig"},{"type":"null"}]},"github":{"anyOf":[{"$ref":"#/components/schemas/AppGitHubSource"},{"type":"null"}],"description":"Repository settings of a GitHub-sourced app"},"defaultDomain":{"description":"Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one","type":["string","null"]},"start":{"description":"How the automatic start after creation went","$ref":"#/components/schemas/AppStartOutcome"},"buildId":{"description":"First build of a GitHub-sourced app; null for image apps","type":["string","null"]}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt","status","config","github","defaultDomain","start","buildId"],"additionalProperties":false},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"AppStatus":{"type":"object","properties":{"state":{"type":"string","enum":["unknown","running","stopped","starting","stopping","scaling","updating","downloading","provisioning","start_blocked","error","building","build_failed"],"description":"Aggregate state; build states win while there is no runnable image"},"targetState":{"type":"string","enum":["running","stopped","unknown"],"description":"What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"},"nodes":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"state":{"type":"string"},"reportedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the server last reported this state; not a start time"}},"required":["address","state","reportedAt"],"additionalProperties":false},"description":"Servers the app is placed on and the container state on each"},"endpoints":{"type":"array","items":{"$ref":"#/components/schemas/AppEndpoint"},"description":"Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"},"errors":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"message":{"type":"string"},"at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"details":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"Extra data the runtime attached, such as the grid or server involved"}},"required":["type","message","at","details"],"additionalProperties":false},"description":"Recent runtime errors, newest first"},"workOrders":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"What the runtime asked the server to do"},"server":{"type":"string","description":"Server address"},"status":{"type":"string"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["type","server","status","createdAt","startedAt","completedAt"],"additionalProperties":false},"description":"The runtime's latest instructions to servers, newest first; an order that never completes is the stall"},"grid":{"anyOf":[{"type":"object","properties":{"fill":{"anyOf":[{"type":"object","properties":{"target":{"type":"integer","description":"Members the grid is composed to have"},"online":{"type":"integer","description":"Members the app runs on"},"pending":{"type":"integer"},"converging":{"type":"boolean","description":"The grid is still acquiring members or placing the app"}},"required":["target","online","pending","converging"],"additionalProperties":false},{"type":"null"}],"description":"How far the grid has filled for this app"},"idleReclaim":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"warnedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"evictAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the app becomes evictable; null when the grid only warns"},"evictedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"cooldownEndsAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the grid takes the app again after an eviction"},"evidence":{"type":"string","description":"Why the grid considers the app idle"}},"required":["gridId","warnedAt","evictAt","evictedAt","cooldownEndsAt","evidence"],"additionalProperties":false},"description":"Grids whose idle policy has warned or evicted the app"},"queue":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"position":{"type":"integer","description":"1-based place in the grid's fair-share queue"},"queueLength":{"type":"integer"},"enqueuedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"held":{"type":"boolean","description":"The scheduler keeps the app off the grid until its turn comes"}},"required":["gridId","position","queueLength","enqueuedAt","held"],"additionalProperties":false},"description":"Full grids the app is queued on"}},"required":["fill","idleReclaim","queue"],"additionalProperties":false},{"type":"null"}],"description":"Grid placement details; null for a serverless app"},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the status again while it is changing; null in a settled state"}},"required":["state","targetState","nodes","endpoints","errors","workOrders","grid","pollAfterSec"],"additionalProperties":false},"AppEndpoint":{"type":"object","properties":{"port":{"type":"integer","description":"Container port, as in config.ports"},"protocol":{"type":"string","description":"Protocol of that port, as in config.ports"},"address":{"description":"An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired","type":["string","null"]},"server":{"description":"Server address of a direct tcp or udp endpoint; null when load-balanced","type":["string","null"]}},"required":["port","protocol","address","server"],"additionalProperties":false},"AppConfig":{"type":"object","properties":{"image":{"type":"string","description":"Container image reference"},"imageTag":{"description":"Tag, kept separately for Docker Hub images","type":["string","null"]},"registryUsername":{"description":"Registry login; the password is write-only","type":["string","null"]},"ports":{"type":"array","items":{"type":"object","properties":{"port":{"type":"integer"},"protocol":{"type":"string"}},"required":["port","protocol"],"additionalProperties":false}},"env":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":["string","null"]},"secret":{"type":"boolean"}},"required":["key","value","secret"],"additionalProperties":false},"description":"Environment variables; value is null for secrets, and on a connection that takes no credentials for values that look like one (null keeps a value on update)"},"command":{"type":"array","items":{"type":"string"}},"entrypoint":{"type":"array","items":{"type":"string"}},"startupCommand":{"anyOf":[{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"user":{"type":"string"},"timeoutSec":{"type":"number"},"startPeriodSec":{"type":"number"}},"required":["args","user","timeoutSec","startPeriodSec"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"anyOf":[{"type":"object","properties":{"test":{"type":"array","items":{"type":"string"}},"intervalSec":{"type":"number"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"anyOf":[{"type":"object","properties":{"mode":{"type":"string"},"startTimeoutSec":{"type":"number"},"maxRetriesPerNode":{"type":"number"},"maxErrorTolerance":{"type":"number"},"numTasks":{"type":"number"}},"required":["mode","startTimeoutSec","maxRetriesPerNode","maxErrorTolerance","numTasks"],"additionalProperties":false},{"type":"null"}]},"instances":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"}},"required":["min","max"],"additionalProperties":false},"stateful":{"type":"boolean"},"hardware":{"type":"object","properties":{"gpuCount":{"type":"integer"},"gpuModels":{"type":"array","items":{"type":"string"}},"gpuManufacturer":{"type":["string","null"]},"gpuVramBytes":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"shmemBytes":{"type":"number"}},"required":["gpuCount","gpuModels","gpuManufacturer","gpuVramBytes","ramBytes","storageBytes","shmemBytes"],"additionalProperties":false},"placement":{"type":"object","properties":{"regions":{"type":"array","items":{"type":"string"},"description":"Kinesis regions, e.g. us-west"},"providers":{"type":"array","items":{"type":"object","properties":{"csp":{"type":"string"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string"},"zones":{"type":"array","items":{"type":"string"}}},"required":["region","zones"],"additionalProperties":false}}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"type":"array","items":{"type":"string"},"description":"Source IPs or CIDR ranges allowed to reach the app"}},"required":["regions","providers","allowedIps"],"additionalProperties":false},"grids":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}},"priorities":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"Priority per grid id"}},"required":["ids","priorities"],"additionalProperties":false},"volumes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"target":{"type":"string"},"declaredSizeBytes":{"type":"number"}},"required":["name","target","declaredSizeBytes"],"additionalProperties":false}},"domains":{"type":"array","items":{"type":"string"},"description":"Custom domains attached to the app (see the domain operations)"}},"required":["image","imageTag","registryUsername","ports","env","command","entrypoint","startupCommand","healthcheck","restartPolicy","instances","stateful","hardware","placement","grids","volumes","domains"],"additionalProperties":false},"AppGitHubSource":{"type":"object","properties":{"installationId":{"type":"integer","description":"GitHub App installation the repository is read through"},"repo":{"type":"string","description":"owner/name"},"url":{"type":"string"},"branch":{"type":"string","description":"Branch that is built and, with autoDeploy, followed"},"contextDir":{"type":"string"},"dockerfilePath":{"description":"Dockerfile path relative to contextDir; not used while dockerfileContent is set","type":["string","null"]},"dockerfileOverride":{"type":"boolean","description":"An inline Dockerfile (dockerfileContent) is built instead of dockerfilePath"},"dockerfileContent":{"description":"The inline Dockerfile that is built; null when the repository's own file is used","type":["string","null"]},"autoDeploy":{"type":"boolean","description":"Every push to the branch rebuilds and redeploys"},"clonedFrom":{"description":"Public repository (owner/name) the app's repository was copied from at creation; null otherwise","type":["string","null"]}},"required":["installationId","repo","url","branch","contextDir","dockerfilePath","dockerfileOverride","dockerfileContent","autoDeploy","clonedFrom"],"additionalProperties":false},"AppStartOutcome":{"type":"object","properties":{"state":{"type":"string","enum":["requested","deferred","building","failed"],"description":"requested: the start was accepted, poll the status to follow it; deferred: machines are being provisioned for the app first, poll the status; building: the app starts once its first build succeeds, poll the build; failed: the app exists but is stopped, see error"},"error":{"description":"Why the automatic start failed","type":["string","null"]}},"required":["state","error"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PaymentRequired":{"description":"The balance or the card does not cover the action: insufficient_balance, balance_over_limit, card_declined, authentication_required or blocked_by_risk","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps":{"post":{"operationId":"apps.create","summary":"Create an app from a container image, or from a GitHub repository with source, and start it. With envFrom the new app takes another app's environment variables, secret values included, copied on the server. Omitted config fields take the dashboard defaults (1 GiB RAM, 1 GB disk, 1 to 3 instances, no GPU).","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps), `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\n\nErrors specific to this operation:\n- 402 `balance_over_limit`: The organization's balance is below the allowed negative limit; add credits and retry\n- 429 `app_creation_quota_exceeded`: The organization's app creation quota is used up; add a payment method or wait for the window\n- 400 `no_suitable_machines`: No machine can run this configuration; lower the hardware requirements or ask for provisioning\n- 400 `serverless_gpu_unavailable`: The app asks for a GPU and targets no grid; serverless placement is CPU-only, so pass grids.ids\n- 429 `grid_quota_exceeded`: The grids the app targets have no room for it under their per-person app and instance quotas\n- 403 `serverless_disabled`: The app would run on Kinesis serverless capacity, which the organization keeps to its admins; target a grid\n- 503 `github_not_configured`: GitHub deployments are not enabled on this server\n- 404 `github_installation_not_found`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\n- 404 `github_repo_not_found`: The installation cannot see the repository; grant the GitHub App access to it\n- 502 `upstream_github`: GitHub did not answer; retry shortly\n- 404 `github_branch_not_found`: The repository has no such branch\n- 409 `app_config_unavailable`: The envFrom app has no configuration to copy from","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project to create the app in","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string of at most 255 characters. A retry with the same key and body returns the first call's response (with Idempotent-Replayed: true) instead of acting twice; a different body is refused with 409 idempotency_conflict, and a retry while the first call is still running gets 409 request_in_progress. Keys are kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^\\S+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"App name"},"config":{"type":"object","properties":{"image":{"description":"Container image reference, e.g. nginx:1.27 or ghcr.io/org/app:tag","type":"string","minLength":1,"maxLength":512},"registryAuth":{"description":"Credentials for a private image","type":"object","properties":{"username":{"type":"string","minLength":1,"description":"Registry login"},"password":{"type":"string","minLength":1,"description":"Registry password or token (write-only)"}},"required":["username","password"],"additionalProperties":false},"ports":{"description":"Ports to expose","maxItems":32,"type":"array","items":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535,"description":"Container port"},"protocol":{"type":"string","enum":["tcp","udp","tcp_load_balancer","tcps_load_balancer","http_load_balancer","https_load_balancer","http80_load_balancer","https443_load_balancer","grpcs_load_balancer"],"description":"How the port is exposed"}},"required":["port","protocol"]}},"env":{"description":"Environment variables","maxItems":200,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]*$","description":"Variable name"},"value":{"description":"Value; null keeps the stored value of an existing variable","type":["string","null"]},"secret":{"description":"Store as a secret: never returned by reads","type":"boolean"}},"required":["key","value"]}},"command":{"description":"Container command (CMD) override","maxItems":256,"type":"array","items":{"type":"string"}},"entrypoint":{"description":"Container entrypoint override","maxItems":256,"type":"array","items":{"type":"string"}},"startupCommand":{"description":"Command run inside the container once it starts; null clears it","anyOf":[{"type":"object","properties":{"args":{"minItems":1,"type":"array","items":{"type":"string"},"description":"Command and arguments"},"user":{"description":"User to run as","type":"string"},"timeoutSec":{"description":"Seconds before the command is considered failed","type":"integer","minimum":0},"startPeriodSec":{"description":"Seconds to wait after the container starts","type":"integer","minimum":0}},"required":["args"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"description":"Container health check; null clears it","anyOf":[{"type":"object","properties":{"test":{"minItems":1,"type":"array","items":{"type":"string"},"description":"Command that must exit 0 for the app to count as healthy"},"intervalSec":{"type":"integer","minimum":1,"description":"Seconds between checks"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"description":"Restart behaviour","type":"object","properties":{"mode":{"type":"string","enum":["disabled","always","on_failure","task_completion"],"description":"When to restart a container that exits"},"startTimeoutSec":{"description":"Seconds a start may take before it counts as failed","type":"integer","minimum":0},"maxRetriesPerNode":{"description":"Restarts allowed on one server before moving on","type":"integer","minimum":0},"maxErrorTolerance":{"description":"Failures tolerated before the app is stopped","type":"integer","minimum":0},"numTasks":{"description":"For task_completion: runs to complete before stopping","type":"integer","minimum":0}},"required":["mode"],"additionalProperties":false},"instances":{"description":"Instance count range","type":"object","properties":{"min":{"type":"integer","minimum":0,"description":"Instances to keep running"},"max":{"type":"integer","minimum":0,"description":"Upper bound for scaling"}},"required":["min","max"],"additionalProperties":false},"stateful":{"description":"Keep each instance pinned to its server and disk","type":"boolean"},"hardware":{"description":"Hardware requirements per instance","type":"object","properties":{"gpuCount":{"description":"GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only","type":"integer","minimum":0,"maximum":16},"gpuModels":{"description":"Acceptable GPU models, e.g. H100; empty means any","maxItems":16,"type":"array","items":{"type":"string","minLength":1}},"gpuManufacturer":{"description":"GPU vendor; null means any","anyOf":[{"type":"string","enum":["amd","apple","intel","nvidia","xilinx"]},{"type":"null"}]},"gpuVramBytes":{"description":"Minimum VRAM per GPU, in bytes","type":"integer","minimum":0},"ramBytes":{"description":"RAM per instance, in bytes","type":"integer","exclusiveMinimum":0},"storageBytes":{"description":"Disk per instance, in bytes","type":"integer","exclusiveMinimum":0},"shmemBytes":{"description":"Shared memory (/dev/shm) size, in bytes","type":"integer","minimum":0}},"additionalProperties":false},"placement":{"description":"Where the app may run and who may reach it","type":"object","properties":{"regions":{"description":"Kinesis regions to run in; empty means any","type":"array","items":{"type":"string","enum":["us-west","us-east","us-central","ca-west","ca-east","ca-central","eu-west","eu-north","eu-east","eu-south","eu-central","eu-south-east","ap-east","ap-south-east","ap-north-east","ap-south"]}},"providers":{"description":"Restrict placement to specific providers and regions","type":"array","items":{"type":"object","properties":{"csp":{"type":"string","enum":["aws","azure","gcp","kinesis"],"description":"Cloud provider"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string","minLength":1,"description":"Provider region"},"zones":{"type":"array","items":{"type":"string"},"description":"Zones within the region; empty means any"}},"required":["region","zones"],"additionalProperties":false},"description":"Regions of the provider to allow"}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"description":"Source IPs or CIDR ranges allowed to reach the app; empty means everyone","maxItems":100,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"grids":{"description":"Run on grids instead of serverless capacity (members only, on grids they may deploy to)","type":"object","properties":{"ids":{"description":"Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"priorities":{"description":"Priority per grid id, -100 to 100","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"integer","minimum":-100,"maximum":100}}},"additionalProperties":false},"volumes":{"description":"Persistent volumes","maxItems":4,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$","description":"Volume name, unique within the app"},"target":{"type":"string","pattern":"^\\/.+","description":"Absolute mount path inside the container"},"declaredSizeBytes":{"description":"Expected size, used for placement; 0 or omitted means unspecified","type":"integer","minimum":0,"maximum":17592186044416}},"required":["name","target"],"additionalProperties":false}}},"additionalProperties":false,"description":"Initial configuration; image is required unless source is set"},"source":{"description":"Build the image from a GitHub repository the caller connected (members of the project's organization); omit to pull config.image","type":"object","properties":{"kind":{"type":"string","const":"github","description":"Build the image from a GitHub repository"},"installationId":{"type":"integer","exclusiveMinimum":0,"description":"GitHub App installation id, from the connected accounts list"},"repo":{"description":"Repository as owner/name; omit with clone","type":"string","pattern":"^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9._-]{1,100}$"},"clone":{"description":"Copy a public repository into the connected account and build from the copy, instead of repo","type":"object","properties":{"sourceRepo":{"type":"string","minLength":3,"maxLength":300,"description":"Public GitHub repository to copy: a github.com URL (optionally .../tree/<branch>) or owner/name"},"sourceRef":{"description":"Branch, tag or commit to copy; default: the source's default branch","type":"string","minLength":1,"maxLength":255},"targetRepoName":{"type":"string","pattern":"^[A-Za-z0-9._-]{1,100}$","description":"Name of the new repository under the connected account (installationId)"},"targetVisibility":{"description":"Visibility of the new repository; private by default","type":"string","enum":["private","public"]}},"required":["sourceRepo","targetRepoName"],"additionalProperties":false},"branch":{"description":"Branch to build and follow; default: the repository's default branch","type":"string","minLength":1,"maxLength":255},"contextDir":{"description":"Directory the image is built from, relative to the repository root (default /)","type":"string","minLength":1,"maxLength":512},"dockerfilePath":{"description":"Dockerfile path relative to contextDir (default Dockerfile)","type":"string","minLength":1,"maxLength":512},"autoDeploy":{"description":"Rebuild and redeploy on every push to the branch (default true)","type":"boolean"},"dockerfileContent":{"description":"Inline Dockerfile built instead of dockerfilePath; how a repository without one is deployed","type":"string","maxLength":65536}},"required":["kind","installationId"],"additionalProperties":false},"envFrom":{"description":"Copy another app's environment variables, secret values included, on the server (developer access to that app); use overrides instead of config.env","type":"object","properties":{"projectId":{"type":"string","pattern":"^[0-9a-fA-F]{24}$","description":"Project of the app to copy from"},"appId":{"type":"string","pattern":"^[0-9a-fA-F]{24}$","description":"App to copy the variables from"},"overrides":{"description":"Variables set on top of the copied ones, by key; a null value keeps the copied value","maxItems":200,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]*$","description":"Variable name"},"value":{"description":"Value; null keeps the copied value (a copied secret stays secret)","type":["string","null"]},"secret":{"description":"Store as a secret: never returned by reads","type":"boolean"}},"required":["key","value"]}}},"required":["projectId","appId"],"additionalProperties":false}},"required":["name","config"]}}}},"responses":{"201":{"description":"Created","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppCreateResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Get an app with its full configuration and last known status

> Requires \`apps:read\` (View apps, their configuration and status).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"AppDetail":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"status":{"description":"Status as last cached; use the status endpoint for a live read","$ref":"#/components/schemas/AppStatus"},"config":{"anyOf":[{"$ref":"#/components/schemas/AppConfig"},{"type":"null"}]},"github":{"anyOf":[{"$ref":"#/components/schemas/AppGitHubSource"},{"type":"null"}],"description":"Repository settings of a GitHub-sourced app"},"defaultDomain":{"description":"Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one","type":["string","null"]}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt","status","config","github","defaultDomain"],"additionalProperties":false},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"AppStatus":{"type":"object","properties":{"state":{"type":"string","enum":["unknown","running","stopped","starting","stopping","scaling","updating","downloading","provisioning","start_blocked","error","building","build_failed"],"description":"Aggregate state; build states win while there is no runnable image"},"targetState":{"type":"string","enum":["running","stopped","unknown"],"description":"What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"},"nodes":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"state":{"type":"string"},"reportedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the server last reported this state; not a start time"}},"required":["address","state","reportedAt"],"additionalProperties":false},"description":"Servers the app is placed on and the container state on each"},"endpoints":{"type":"array","items":{"$ref":"#/components/schemas/AppEndpoint"},"description":"Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"},"errors":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"message":{"type":"string"},"at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"details":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"Extra data the runtime attached, such as the grid or server involved"}},"required":["type","message","at","details"],"additionalProperties":false},"description":"Recent runtime errors, newest first"},"workOrders":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"What the runtime asked the server to do"},"server":{"type":"string","description":"Server address"},"status":{"type":"string"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["type","server","status","createdAt","startedAt","completedAt"],"additionalProperties":false},"description":"The runtime's latest instructions to servers, newest first; an order that never completes is the stall"},"grid":{"anyOf":[{"type":"object","properties":{"fill":{"anyOf":[{"type":"object","properties":{"target":{"type":"integer","description":"Members the grid is composed to have"},"online":{"type":"integer","description":"Members the app runs on"},"pending":{"type":"integer"},"converging":{"type":"boolean","description":"The grid is still acquiring members or placing the app"}},"required":["target","online","pending","converging"],"additionalProperties":false},{"type":"null"}],"description":"How far the grid has filled for this app"},"idleReclaim":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"warnedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"evictAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the app becomes evictable; null when the grid only warns"},"evictedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"cooldownEndsAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the grid takes the app again after an eviction"},"evidence":{"type":"string","description":"Why the grid considers the app idle"}},"required":["gridId","warnedAt","evictAt","evictedAt","cooldownEndsAt","evidence"],"additionalProperties":false},"description":"Grids whose idle policy has warned or evicted the app"},"queue":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"position":{"type":"integer","description":"1-based place in the grid's fair-share queue"},"queueLength":{"type":"integer"},"enqueuedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"held":{"type":"boolean","description":"The scheduler keeps the app off the grid until its turn comes"}},"required":["gridId","position","queueLength","enqueuedAt","held"],"additionalProperties":false},"description":"Full grids the app is queued on"}},"required":["fill","idleReclaim","queue"],"additionalProperties":false},{"type":"null"}],"description":"Grid placement details; null for a serverless app"},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the status again while it is changing; null in a settled state"}},"required":["state","targetState","nodes","endpoints","errors","workOrders","grid","pollAfterSec"],"additionalProperties":false},"AppEndpoint":{"type":"object","properties":{"port":{"type":"integer","description":"Container port, as in config.ports"},"protocol":{"type":"string","description":"Protocol of that port, as in config.ports"},"address":{"description":"An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired","type":["string","null"]},"server":{"description":"Server address of a direct tcp or udp endpoint; null when load-balanced","type":["string","null"]}},"required":["port","protocol","address","server"],"additionalProperties":false},"AppConfig":{"type":"object","properties":{"image":{"type":"string","description":"Container image reference"},"imageTag":{"description":"Tag, kept separately for Docker Hub images","type":["string","null"]},"registryUsername":{"description":"Registry login; the password is write-only","type":["string","null"]},"ports":{"type":"array","items":{"type":"object","properties":{"port":{"type":"integer"},"protocol":{"type":"string"}},"required":["port","protocol"],"additionalProperties":false}},"env":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":["string","null"]},"secret":{"type":"boolean"}},"required":["key","value","secret"],"additionalProperties":false},"description":"Environment variables; value is null for secrets, and on a connection that takes no credentials for values that look like one (null keeps a value on update)"},"command":{"type":"array","items":{"type":"string"}},"entrypoint":{"type":"array","items":{"type":"string"}},"startupCommand":{"anyOf":[{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"user":{"type":"string"},"timeoutSec":{"type":"number"},"startPeriodSec":{"type":"number"}},"required":["args","user","timeoutSec","startPeriodSec"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"anyOf":[{"type":"object","properties":{"test":{"type":"array","items":{"type":"string"}},"intervalSec":{"type":"number"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"anyOf":[{"type":"object","properties":{"mode":{"type":"string"},"startTimeoutSec":{"type":"number"},"maxRetriesPerNode":{"type":"number"},"maxErrorTolerance":{"type":"number"},"numTasks":{"type":"number"}},"required":["mode","startTimeoutSec","maxRetriesPerNode","maxErrorTolerance","numTasks"],"additionalProperties":false},{"type":"null"}]},"instances":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"}},"required":["min","max"],"additionalProperties":false},"stateful":{"type":"boolean"},"hardware":{"type":"object","properties":{"gpuCount":{"type":"integer"},"gpuModels":{"type":"array","items":{"type":"string"}},"gpuManufacturer":{"type":["string","null"]},"gpuVramBytes":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"shmemBytes":{"type":"number"}},"required":["gpuCount","gpuModels","gpuManufacturer","gpuVramBytes","ramBytes","storageBytes","shmemBytes"],"additionalProperties":false},"placement":{"type":"object","properties":{"regions":{"type":"array","items":{"type":"string"},"description":"Kinesis regions, e.g. us-west"},"providers":{"type":"array","items":{"type":"object","properties":{"csp":{"type":"string"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string"},"zones":{"type":"array","items":{"type":"string"}}},"required":["region","zones"],"additionalProperties":false}}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"type":"array","items":{"type":"string"},"description":"Source IPs or CIDR ranges allowed to reach the app"}},"required":["regions","providers","allowedIps"],"additionalProperties":false},"grids":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}},"priorities":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"Priority per grid id"}},"required":["ids","priorities"],"additionalProperties":false},"volumes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"target":{"type":"string"},"declaredSizeBytes":{"type":"number"}},"required":["name","target","declaredSizeBytes"],"additionalProperties":false}},"domains":{"type":"array","items":{"type":"string"},"description":"Custom domains attached to the app (see the domain operations)"}},"required":["image","imageTag","registryUsername","ports","env","command","entrypoint","startupCommand","healthcheck","restartPolicy","instances","stateful","hardware","placement","grids","volumes","domains"],"additionalProperties":false},"AppGitHubSource":{"type":"object","properties":{"installationId":{"type":"integer","description":"GitHub App installation the repository is read through"},"repo":{"type":"string","description":"owner/name"},"url":{"type":"string"},"branch":{"type":"string","description":"Branch that is built and, with autoDeploy, followed"},"contextDir":{"type":"string"},"dockerfilePath":{"description":"Dockerfile path relative to contextDir; not used while dockerfileContent is set","type":["string","null"]},"dockerfileOverride":{"type":"boolean","description":"An inline Dockerfile (dockerfileContent) is built instead of dockerfilePath"},"dockerfileContent":{"description":"The inline Dockerfile that is built; null when the repository's own file is used","type":["string","null"]},"autoDeploy":{"type":"boolean","description":"Every push to the branch rebuilds and redeploys"},"clonedFrom":{"description":"Public repository (owner/name) the app's repository was copied from at creation; null otherwise","type":["string","null"]}},"required":["installationId","repo","url","branch","contextDir","dockerfilePath","dockerfileOverride","dockerfileContent","autoDeploy","clonedFrom"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}":{"get":{"operationId":"apps.get","summary":"Get an app with its full configuration and last known status","description":"Requires `apps:read` (View apps, their configuration and status).","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppDetail"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Update the name, alerts, configuration or repository settings of an app. A running app is redeployed with the new configuration; while a GitHub-sourced app is still building, the change is applied by that build. Changing instances, hardware or volumes also needs compute:spend.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps).\
> \
> Also requires \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances) when instances, hardware or volumes change.\
> \
> Also requires \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances) when source changes what is built (branch, contextDir, dockerfilePath, dockerfileContent).\
> \
> Errors specific to this operation:\
> \- 409 \`app\_source\_unsupported\`: The app's image is uploaded from the dashboard, or source was given for an image app\
> \- 400 \`grid\_to\_serverless\`: A grid app cannot move back to serverless\
> \- 400 \`no\_suitable\_machines\`: No machine can run the new configuration; lower the hardware requirements\
> \- 400 \`serverless\_gpu\_unavailable\`: The app would ask for a GPU while targeting no grid; serverless placement is CPU-only, so pass grids.ids or set hardware.gpuCount to 0 (a serverless app saved with a GPU earlier is refused on its next update too)\
> \- 429 \`grid\_quota\_exceeded\`: The grids the app targets have no room for it under their per-person app and instance quotas\
> \- 403 \`serverless\_disabled\`: The change needs new serverless machines, or starts a build of a serverless app, and the organization keeps serverless capacity to its admins; target a grid

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"AppUpdateResult":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"status":{"description":"Status as last cached; use the status endpoint for a live read","$ref":"#/components/schemas/AppStatus"},"config":{"anyOf":[{"$ref":"#/components/schemas/AppConfig"},{"type":"null"}]},"github":{"anyOf":[{"$ref":"#/components/schemas/AppGitHubSource"},{"type":"null"}],"description":"Repository settings of a GitHub-sourced app"},"defaultDomain":{"description":"Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one","type":["string","null"]},"buildId":{"description":"Build started by a source change; null when nothing was rebuilt","type":["string","null"]}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt","status","config","github","defaultDomain","buildId"],"additionalProperties":false},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"AppStatus":{"type":"object","properties":{"state":{"type":"string","enum":["unknown","running","stopped","starting","stopping","scaling","updating","downloading","provisioning","start_blocked","error","building","build_failed"],"description":"Aggregate state; build states win while there is no runnable image"},"targetState":{"type":"string","enum":["running","stopped","unknown"],"description":"What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"},"nodes":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"state":{"type":"string"},"reportedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the server last reported this state; not a start time"}},"required":["address","state","reportedAt"],"additionalProperties":false},"description":"Servers the app is placed on and the container state on each"},"endpoints":{"type":"array","items":{"$ref":"#/components/schemas/AppEndpoint"},"description":"Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"},"errors":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"message":{"type":"string"},"at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"details":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"Extra data the runtime attached, such as the grid or server involved"}},"required":["type","message","at","details"],"additionalProperties":false},"description":"Recent runtime errors, newest first"},"workOrders":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"What the runtime asked the server to do"},"server":{"type":"string","description":"Server address"},"status":{"type":"string"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["type","server","status","createdAt","startedAt","completedAt"],"additionalProperties":false},"description":"The runtime's latest instructions to servers, newest first; an order that never completes is the stall"},"grid":{"anyOf":[{"type":"object","properties":{"fill":{"anyOf":[{"type":"object","properties":{"target":{"type":"integer","description":"Members the grid is composed to have"},"online":{"type":"integer","description":"Members the app runs on"},"pending":{"type":"integer"},"converging":{"type":"boolean","description":"The grid is still acquiring members or placing the app"}},"required":["target","online","pending","converging"],"additionalProperties":false},{"type":"null"}],"description":"How far the grid has filled for this app"},"idleReclaim":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"warnedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"evictAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the app becomes evictable; null when the grid only warns"},"evictedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"cooldownEndsAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the grid takes the app again after an eviction"},"evidence":{"type":"string","description":"Why the grid considers the app idle"}},"required":["gridId","warnedAt","evictAt","evictedAt","cooldownEndsAt","evidence"],"additionalProperties":false},"description":"Grids whose idle policy has warned or evicted the app"},"queue":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"position":{"type":"integer","description":"1-based place in the grid's fair-share queue"},"queueLength":{"type":"integer"},"enqueuedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"held":{"type":"boolean","description":"The scheduler keeps the app off the grid until its turn comes"}},"required":["gridId","position","queueLength","enqueuedAt","held"],"additionalProperties":false},"description":"Full grids the app is queued on"}},"required":["fill","idleReclaim","queue"],"additionalProperties":false},{"type":"null"}],"description":"Grid placement details; null for a serverless app"},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the status again while it is changing; null in a settled state"}},"required":["state","targetState","nodes","endpoints","errors","workOrders","grid","pollAfterSec"],"additionalProperties":false},"AppEndpoint":{"type":"object","properties":{"port":{"type":"integer","description":"Container port, as in config.ports"},"protocol":{"type":"string","description":"Protocol of that port, as in config.ports"},"address":{"description":"An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired","type":["string","null"]},"server":{"description":"Server address of a direct tcp or udp endpoint; null when load-balanced","type":["string","null"]}},"required":["port","protocol","address","server"],"additionalProperties":false},"AppConfig":{"type":"object","properties":{"image":{"type":"string","description":"Container image reference"},"imageTag":{"description":"Tag, kept separately for Docker Hub images","type":["string","null"]},"registryUsername":{"description":"Registry login; the password is write-only","type":["string","null"]},"ports":{"type":"array","items":{"type":"object","properties":{"port":{"type":"integer"},"protocol":{"type":"string"}},"required":["port","protocol"],"additionalProperties":false}},"env":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":["string","null"]},"secret":{"type":"boolean"}},"required":["key","value","secret"],"additionalProperties":false},"description":"Environment variables; value is null for secrets, and on a connection that takes no credentials for values that look like one (null keeps a value on update)"},"command":{"type":"array","items":{"type":"string"}},"entrypoint":{"type":"array","items":{"type":"string"}},"startupCommand":{"anyOf":[{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"user":{"type":"string"},"timeoutSec":{"type":"number"},"startPeriodSec":{"type":"number"}},"required":["args","user","timeoutSec","startPeriodSec"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"anyOf":[{"type":"object","properties":{"test":{"type":"array","items":{"type":"string"}},"intervalSec":{"type":"number"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"anyOf":[{"type":"object","properties":{"mode":{"type":"string"},"startTimeoutSec":{"type":"number"},"maxRetriesPerNode":{"type":"number"},"maxErrorTolerance":{"type":"number"},"numTasks":{"type":"number"}},"required":["mode","startTimeoutSec","maxRetriesPerNode","maxErrorTolerance","numTasks"],"additionalProperties":false},{"type":"null"}]},"instances":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"}},"required":["min","max"],"additionalProperties":false},"stateful":{"type":"boolean"},"hardware":{"type":"object","properties":{"gpuCount":{"type":"integer"},"gpuModels":{"type":"array","items":{"type":"string"}},"gpuManufacturer":{"type":["string","null"]},"gpuVramBytes":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"shmemBytes":{"type":"number"}},"required":["gpuCount","gpuModels","gpuManufacturer","gpuVramBytes","ramBytes","storageBytes","shmemBytes"],"additionalProperties":false},"placement":{"type":"object","properties":{"regions":{"type":"array","items":{"type":"string"},"description":"Kinesis regions, e.g. us-west"},"providers":{"type":"array","items":{"type":"object","properties":{"csp":{"type":"string"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string"},"zones":{"type":"array","items":{"type":"string"}}},"required":["region","zones"],"additionalProperties":false}}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"type":"array","items":{"type":"string"},"description":"Source IPs or CIDR ranges allowed to reach the app"}},"required":["regions","providers","allowedIps"],"additionalProperties":false},"grids":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}},"priorities":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"Priority per grid id"}},"required":["ids","priorities"],"additionalProperties":false},"volumes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"target":{"type":"string"},"declaredSizeBytes":{"type":"number"}},"required":["name","target","declaredSizeBytes"],"additionalProperties":false}},"domains":{"type":"array","items":{"type":"string"},"description":"Custom domains attached to the app (see the domain operations)"}},"required":["image","imageTag","registryUsername","ports","env","command","entrypoint","startupCommand","healthcheck","restartPolicy","instances","stateful","hardware","placement","grids","volumes","domains"],"additionalProperties":false},"AppGitHubSource":{"type":"object","properties":{"installationId":{"type":"integer","description":"GitHub App installation the repository is read through"},"repo":{"type":"string","description":"owner/name"},"url":{"type":"string"},"branch":{"type":"string","description":"Branch that is built and, with autoDeploy, followed"},"contextDir":{"type":"string"},"dockerfilePath":{"description":"Dockerfile path relative to contextDir; not used while dockerfileContent is set","type":["string","null"]},"dockerfileOverride":{"type":"boolean","description":"An inline Dockerfile (dockerfileContent) is built instead of dockerfilePath"},"dockerfileContent":{"description":"The inline Dockerfile that is built; null when the repository's own file is used","type":["string","null"]},"autoDeploy":{"type":"boolean","description":"Every push to the branch rebuilds and redeploys"},"clonedFrom":{"description":"Public repository (owner/name) the app's repository was copied from at creation; null otherwise","type":["string","null"]}},"required":["installationId","repo","url","branch","contextDir","dockerfilePath","dockerfileOverride","dockerfileContent","autoDeploy","clonedFrom"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}":{"patch":{"operationId":"apps.update","summary":"Update the name, alerts, configuration or repository settings of an app. A running app is redeployed with the new configuration; while a GitHub-sourced app is still building, the change is applied by that build. Changing instances, hardware or volumes also needs compute:spend.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps).\n\nAlso requires `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances) when instances, hardware or volumes change.\n\nAlso requires `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances) when source changes what is built (branch, contextDir, dockerfilePath, dockerfileContent).\n\nErrors specific to this operation:\n- 409 `app_source_unsupported`: The app's image is uploaded from the dashboard, or source was given for an image app\n- 400 `grid_to_serverless`: A grid app cannot move back to serverless\n- 400 `no_suitable_machines`: No machine can run the new configuration; lower the hardware requirements\n- 400 `serverless_gpu_unavailable`: The app would ask for a GPU while targeting no grid; serverless placement is CPU-only, so pass grids.ids or set hardware.gpuCount to 0 (a serverless app saved with a GPU earlier is refused on its next update too)\n- 429 `grid_quota_exceeded`: The grids the app targets have no room for it under their per-person app and instance quotas\n- 403 `serverless_disabled`: The change needs new serverless machines, or starts a build of a serverless app, and the organization keeps serverless capacity to its admins; target a grid","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"New app name","type":"string","minLength":1,"maxLength":100},"alerts":{"description":"Health-check email alerts","type":"object","properties":{"enabled":{"type":"boolean","description":"Send an email when the app is unhealthy"},"emails":{"maxItems":10,"type":"array","items":{"type":"string","format":"email"},"description":"Recipients"},"delayMinutes":{"type":"integer","minimum":0,"maximum":30,"description":"Minutes the app must stay unhealthy before an email"}},"required":["enabled","emails","delayMinutes"],"additionalProperties":false},"config":{"description":"Configuration changes; list fields replace the stored list, object fields merge per key","type":"object","properties":{"image":{"description":"Container image reference, e.g. nginx:1.27 or ghcr.io/org/app:tag","type":"string","minLength":1,"maxLength":512},"registryAuth":{"description":"Credentials for a private image","type":"object","properties":{"username":{"type":"string","minLength":1,"description":"Registry login"},"password":{"type":"string","minLength":1,"description":"Registry password or token (write-only)"}},"required":["username","password"],"additionalProperties":false},"ports":{"description":"Ports to expose","maxItems":32,"type":"array","items":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535,"description":"Container port"},"protocol":{"type":"string","enum":["tcp","udp","tcp_load_balancer","tcps_load_balancer","http_load_balancer","https_load_balancer","http80_load_balancer","https443_load_balancer","grpcs_load_balancer"],"description":"How the port is exposed"}},"required":["port","protocol"]}},"env":{"description":"Environment variables","maxItems":200,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]*$","description":"Variable name"},"value":{"description":"Value; null keeps the stored value of an existing variable","type":["string","null"]},"secret":{"description":"Store as a secret: never returned by reads","type":"boolean"}},"required":["key","value"]}},"command":{"description":"Container command (CMD) override","maxItems":256,"type":"array","items":{"type":"string"}},"entrypoint":{"description":"Container entrypoint override","maxItems":256,"type":"array","items":{"type":"string"}},"startupCommand":{"description":"Command run inside the container once it starts; null clears it","anyOf":[{"type":"object","properties":{"args":{"minItems":1,"type":"array","items":{"type":"string"},"description":"Command and arguments"},"user":{"description":"User to run as","type":"string"},"timeoutSec":{"description":"Seconds before the command is considered failed","type":"integer","minimum":0},"startPeriodSec":{"description":"Seconds to wait after the container starts","type":"integer","minimum":0}},"required":["args"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"description":"Container health check; null clears it","anyOf":[{"type":"object","properties":{"test":{"minItems":1,"type":"array","items":{"type":"string"},"description":"Command that must exit 0 for the app to count as healthy"},"intervalSec":{"type":"integer","minimum":1,"description":"Seconds between checks"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"description":"Restart behaviour","type":"object","properties":{"mode":{"type":"string","enum":["disabled","always","on_failure","task_completion"],"description":"When to restart a container that exits"},"startTimeoutSec":{"description":"Seconds a start may take before it counts as failed","type":"integer","minimum":0},"maxRetriesPerNode":{"description":"Restarts allowed on one server before moving on","type":"integer","minimum":0},"maxErrorTolerance":{"description":"Failures tolerated before the app is stopped","type":"integer","minimum":0},"numTasks":{"description":"For task_completion: runs to complete before stopping","type":"integer","minimum":0}},"required":["mode"],"additionalProperties":false},"instances":{"description":"Instance count range","type":"object","properties":{"min":{"type":"integer","minimum":0,"description":"Instances to keep running"},"max":{"type":"integer","minimum":0,"description":"Upper bound for scaling"}},"required":["min","max"],"additionalProperties":false},"stateful":{"description":"Keep each instance pinned to its server and disk","type":"boolean"},"hardware":{"description":"Hardware requirements per instance","type":"object","properties":{"gpuCount":{"description":"GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only","type":"integer","minimum":0,"maximum":16},"gpuModels":{"description":"Acceptable GPU models, e.g. H100; empty means any","maxItems":16,"type":"array","items":{"type":"string","minLength":1}},"gpuManufacturer":{"description":"GPU vendor; null means any","anyOf":[{"type":"string","enum":["amd","apple","intel","nvidia","xilinx"]},{"type":"null"}]},"gpuVramBytes":{"description":"Minimum VRAM per GPU, in bytes","type":"integer","minimum":0},"ramBytes":{"description":"RAM per instance, in bytes","type":"integer","exclusiveMinimum":0},"storageBytes":{"description":"Disk per instance, in bytes","type":"integer","exclusiveMinimum":0},"shmemBytes":{"description":"Shared memory (/dev/shm) size, in bytes","type":"integer","minimum":0}},"additionalProperties":false},"placement":{"description":"Where the app may run and who may reach it","type":"object","properties":{"regions":{"description":"Kinesis regions to run in; empty means any","type":"array","items":{"type":"string","enum":["us-west","us-east","us-central","ca-west","ca-east","ca-central","eu-west","eu-north","eu-east","eu-south","eu-central","eu-south-east","ap-east","ap-south-east","ap-north-east","ap-south"]}},"providers":{"description":"Restrict placement to specific providers and regions","type":"array","items":{"type":"object","properties":{"csp":{"type":"string","enum":["aws","azure","gcp","kinesis"],"description":"Cloud provider"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string","minLength":1,"description":"Provider region"},"zones":{"type":"array","items":{"type":"string"},"description":"Zones within the region; empty means any"}},"required":["region","zones"],"additionalProperties":false},"description":"Regions of the provider to allow"}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"description":"Source IPs or CIDR ranges allowed to reach the app; empty means everyone","maxItems":100,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"grids":{"description":"Run on grids instead of serverless capacity (members only, on grids they may deploy to)","type":"object","properties":{"ids":{"description":"Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"priorities":{"description":"Priority per grid id, -100 to 100","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"integer","minimum":-100,"maximum":100}}},"additionalProperties":false},"volumes":{"description":"Persistent volumes","maxItems":4,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$","description":"Volume name, unique within the app"},"target":{"type":"string","pattern":"^\\/.+","description":"Absolute mount path inside the container"},"declaredSizeBytes":{"description":"Expected size, used for placement; 0 or omitted means unspecified","type":"integer","minimum":0,"maximum":17592186044416}},"required":["name","target"],"additionalProperties":false}}},"additionalProperties":false},"source":{"description":"Repository settings of a GitHub-sourced app (members of the project's organization); a change to the branch, directories or Dockerfile starts a rebuild","type":"object","properties":{"branch":{"description":"Branch to build and follow; default: the repository's default branch","type":"string","minLength":1,"maxLength":255},"contextDir":{"description":"Directory the image is built from, relative to the repository root (default /)","type":"string","minLength":1,"maxLength":512},"dockerfilePath":{"description":"Dockerfile path relative to contextDir (default Dockerfile)","type":"string","minLength":1,"maxLength":512},"autoDeploy":{"description":"Rebuild and redeploy on every push to the branch (default true)","type":"boolean"},"dockerfileContent":{"description":"Inline Dockerfile used instead of dockerfilePath; null goes back to the file in the repository","anyOf":[{"type":"string","maxLength":65536},{"type":"null"}]}},"additionalProperties":false}}}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppUpdateResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Delete a stopped app (admins, or its creator). Fails with app\_running while any node is still live. Irreversible.

> Requires \`apps:admin\` (Delete or move apps and manage who they are shared with).\
> \
> Errors specific to this operation:\
> \- 409 \`app\_running\`: A node is still running or stopping the app; stop it and retry once the status reports stopped

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}/apps/{appId}":{"delete":{"operationId":"apps.delete","summary":"Delete a stopped app (admins, or its creator). Fails with app_running while any node is still live. Irreversible.","description":"Requires `apps:admin` (Delete or move apps and manage who they are shared with).\n\nErrors specific to this operation:\n- 409 `app_running`: A node is still running or stopping the app; stop it and retry once the status reports stopped","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Id of the deleted app"},"deleted":{"type":"boolean","const":true}},"required":["id","deleted"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Read the live status of an app from the runtime. Aggregate state, each server's container state, where each port answers, recent errors. Poll this after start, stop or delete.

> Requires \`apps:read\` (View apps, their configuration and status).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"AppEndpoint":{"type":"object","properties":{"port":{"type":"integer","description":"Container port, as in config.ports"},"protocol":{"type":"string","description":"Protocol of that port, as in config.ports"},"address":{"description":"An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired","type":["string","null"]},"server":{"description":"Server address of a direct tcp or udp endpoint; null when load-balanced","type":["string","null"]}},"required":["port","protocol","address","server"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/status":{"get":{"operationId":"apps.status","summary":"Read the live status of an app from the runtime. Aggregate state, each server's container state, where each port answers, recent errors. Poll this after start, stop or delete.","description":"Requires `apps:read` (View apps, their configuration and status).","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","enum":["unknown","running","stopped","starting","stopping","scaling","updating","downloading","provisioning","start_blocked","error","building","build_failed"],"description":"Aggregate state; build states win while there is no runnable image"},"targetState":{"type":"string","enum":["running","stopped","unknown"],"description":"What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"},"nodes":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"state":{"type":"string"},"reportedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the server last reported this state; not a start time"}},"required":["address","state","reportedAt"],"additionalProperties":false},"description":"Servers the app is placed on and the container state on each"},"endpoints":{"type":"array","items":{"$ref":"#/components/schemas/AppEndpoint"},"description":"Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"},"errors":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"message":{"type":"string"},"at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"details":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"Extra data the runtime attached, such as the grid or server involved"}},"required":["type","message","at","details"],"additionalProperties":false},"description":"Recent runtime errors, newest first"},"workOrders":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"What the runtime asked the server to do"},"server":{"type":"string","description":"Server address"},"status":{"type":"string"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["type","server","status","createdAt","startedAt","completedAt"],"additionalProperties":false},"description":"The runtime's latest instructions to servers, newest first; an order that never completes is the stall"},"grid":{"anyOf":[{"type":"object","properties":{"fill":{"anyOf":[{"type":"object","properties":{"target":{"type":"integer","description":"Members the grid is composed to have"},"online":{"type":"integer","description":"Members the app runs on"},"pending":{"type":"integer"},"converging":{"type":"boolean","description":"The grid is still acquiring members or placing the app"}},"required":["target","online","pending","converging"],"additionalProperties":false},{"type":"null"}],"description":"How far the grid has filled for this app"},"idleReclaim":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"warnedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"evictAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the app becomes evictable; null when the grid only warns"},"evictedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"cooldownEndsAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the grid takes the app again after an eviction"},"evidence":{"type":"string","description":"Why the grid considers the app idle"}},"required":["gridId","warnedAt","evictAt","evictedAt","cooldownEndsAt","evidence"],"additionalProperties":false},"description":"Grids whose idle policy has warned or evicted the app"},"queue":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"position":{"type":"integer","description":"1-based place in the grid's fair-share queue"},"queueLength":{"type":"integer"},"enqueuedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"held":{"type":"boolean","description":"The scheduler keeps the app off the grid until its turn comes"}},"required":["gridId","position","queueLength","enqueuedAt","held"],"additionalProperties":false},"description":"Full grids the app is queued on"}},"required":["fill","idleReclaim","queue"],"additionalProperties":false},{"type":"null"}],"description":"Grid placement details; null for a serverless app"},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the status again while it is changing; null in a settled state"},"id":{"type":"string","description":"App id"}},"required":["state","targetState","nodes","endpoints","errors","workOrders","grid","pollAfterSec","id"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Check which servers could run an existing app: with its stored configuration, or with config layered on top to test a change before update\_app. Nothing is changed.

> Requires \`apps:read\` (View apps, their configuration and status).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Placement":{"type":"object","properties":{"placeable":{"type":"boolean","description":"A server can take the app now, a hibernated one would fit once woken, or one can be provisioned"},"serversWithCapacity":{"type":"integer","description":"Servers that match and have room now"},"serversWithoutCapacity":{"type":"integer","description":"Servers that match but are full"},"wakeable":{"type":"integer","description":"Hibernated servers that would match once woken (power_servers wake, or the automatic wake-up on start)"},"wakeableAwaitingStock":{"type":"integer","description":"Of wakeable, servers whose provider has no stock right now, so a wake would not land yet"},"provisionable":{"type":"boolean","description":"The platform can provision a matching cloud machine on demand for a serverless app"},"rejections":{"type":"array","items":{"type":"object","properties":{"server":{"type":"string","description":"Server address"},"reason":{"type":"string"}},"required":["server","reason"],"additionalProperties":false},"description":"Why sampled servers cannot take the app; each distinct reason appears first, then repeats"}},"required":["placeable","serversWithCapacity","serversWithoutCapacity","wakeable","wakeableAwaitingStock","provisionable","rejections"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/placement":{"post":{"operationId":"apps.placement","summary":"Check which servers could run an existing app: with its stored configuration, or with config layered on top to test a change before update_app. Nothing is changed.","description":"Requires `apps:read` (View apps, their configuration and status).","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"config":{"description":"Changes to test, merged over the app's stored configuration","type":"object","properties":{"instances":{"description":"Instance count range","type":"object","properties":{"min":{"type":"integer","minimum":0,"description":"Instances to keep running"},"max":{"type":"integer","minimum":0,"description":"Upper bound for scaling"}},"required":["min","max"],"additionalProperties":false},"stateful":{"description":"Keep each instance pinned to its server and disk","type":"boolean"},"hardware":{"description":"Hardware requirements per instance","type":"object","properties":{"gpuCount":{"description":"GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only","type":"integer","minimum":0,"maximum":16},"gpuModels":{"description":"Acceptable GPU models, e.g. H100; empty means any","maxItems":16,"type":"array","items":{"type":"string","minLength":1}},"gpuManufacturer":{"description":"GPU vendor; null means any","anyOf":[{"type":"string","enum":["amd","apple","intel","nvidia","xilinx"]},{"type":"null"}]},"gpuVramBytes":{"description":"Minimum VRAM per GPU, in bytes","type":"integer","minimum":0},"ramBytes":{"description":"RAM per instance, in bytes","type":"integer","exclusiveMinimum":0},"storageBytes":{"description":"Disk per instance, in bytes","type":"integer","exclusiveMinimum":0},"shmemBytes":{"description":"Shared memory (/dev/shm) size, in bytes","type":"integer","minimum":0}},"additionalProperties":false},"placement":{"description":"Where the app may run and who may reach it","type":"object","properties":{"regions":{"description":"Kinesis regions to run in; empty means any","type":"array","items":{"type":"string","enum":["us-west","us-east","us-central","ca-west","ca-east","ca-central","eu-west","eu-north","eu-east","eu-south","eu-central","eu-south-east","ap-east","ap-south-east","ap-north-east","ap-south"]}},"providers":{"description":"Restrict placement to specific providers and regions","type":"array","items":{"type":"object","properties":{"csp":{"type":"string","enum":["aws","azure","gcp","kinesis"],"description":"Cloud provider"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string","minLength":1,"description":"Provider region"},"zones":{"type":"array","items":{"type":"string"},"description":"Zones within the region; empty means any"}},"required":["region","zones"],"additionalProperties":false},"description":"Regions of the provider to allow"}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"description":"Source IPs or CIDR ranges allowed to reach the app; empty means everyone","maxItems":100,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"grids":{"description":"Run on grids instead of serverless capacity (members only, on grids they may deploy to)","type":"object","properties":{"ids":{"description":"Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"priorities":{"description":"Priority per grid id, -100 to 100","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"integer","minimum":-100,"maximum":100}}},"additionalProperties":false},"volumes":{"description":"Persistent volumes","maxItems":4,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$","description":"Volume name, unique within the app"},"target":{"type":"string","pattern":"^\\/.+","description":"Absolute mount path inside the container"},"declaredSizeBytes":{"description":"Expected size, used for placement; 0 or omitted means unspecified","type":"integer","minimum":0,"maximum":17592186044416}},"required":["name","target"],"additionalProperties":false}}},"additionalProperties":false}}}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Placement"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Ask the runtime to start an app. Returns once the request is accepted; poll the status to follow it.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps), \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\
> \
> Errors specific to this operation:\
> \- 402 \`balance\_over\_limit\`: The organization's balance is below the allowed negative limit; add credits and retry\
> \- 429 \`grid\_quota\_exceeded\`: The grids the app targets have no room for it under their per-person app and instance quotas\
> \- 403 \`serverless\_disabled\`: The app would run on Kinesis serverless capacity, which the organization keeps to its admins; target a grid

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PaymentRequired":{"description":"The balance or the card does not cover the action: insufficient_balance, balance_over_limit, card_declined, authentication_required or blocked_by_risk","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}/apps/{appId}/start":{"post":{"operationId":"apps.start","summary":"Ask the runtime to start an app. Returns once the request is accepted; poll the status to follow it.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps), `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\n\nErrors specific to this operation:\n- 402 `balance_over_limit`: The organization's balance is below the allowed negative limit; add credits and retry\n- 429 `grid_quota_exceeded`: The grids the app targets have no room for it under their per-person app and instance quotas\n- 403 `serverless_disabled`: The app would run on Kinesis serverless capacity, which the organization keeps to its admins; target a grid","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"202":{"description":"Accepted","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"targetState":{"type":"string","enum":["running","stopped"],"description":"State the runtime is now converging on"}},"required":["id","targetState"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Ask the runtime to stop an app. Returns once the request is accepted; poll the status to follow it.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}/apps/{appId}/stop":{"post":{"operationId":"apps.stop","summary":"Ask the runtime to stop an app. Returns once the request is accepted; poll the status to follow it.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps).","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"202":{"description":"Accepted","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"targetState":{"type":"string","enum":["running","stopped"],"description":"State the runtime is now converging on"}},"required":["id","targetState"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Stop an app, wait for it to report stopped, then start it again. Waits up to waitSec (default 30); if the app is still stopping by then the start is issued anyway. Poll the status to follow the start.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps), \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\
> \
> Errors specific to this operation:\
> \- 402 \`balance\_over\_limit\`: The organization's balance is below the allowed negative limit; add credits and retry\
> \- 429 \`grid\_quota\_exceeded\`: The grids the app targets have no room for it under their per-person app and instance quotas\
> \- 403 \`serverless\_disabled\`: The app would run on Kinesis serverless capacity, which the organization keeps to its admins; target a grid

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PaymentRequired":{"description":"The balance or the card does not cover the action: insufficient_balance, balance_over_limit, card_declined, authentication_required or blocked_by_risk","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}/apps/{appId}/restart":{"post":{"operationId":"apps.restart","summary":"Stop an app, wait for it to report stopped, then start it again. Waits up to waitSec (default 30); if the app is still stopping by then the start is issued anyway. Poll the status to follow the start.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps), `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\n\nErrors specific to this operation:\n- 402 `balance_over_limit`: The organization's balance is below the allowed negative limit; add credits and retry\n- 429 `grid_quota_exceeded`: The grids the app targets have no room for it under their per-person app and instance quotas\n- 403 `serverless_disabled`: The app would run on Kinesis serverless capacity, which the organization keeps to its admins; target a grid","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"waitSec":{"description":"Seconds to wait for the stop before starting, 0 to 180 (default 30)","type":"integer","minimum":0,"maximum":180}}}}}},"responses":{"202":{"description":"Accepted","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"targetState":{"type":"string","const":"running","description":"State the runtime is now converging on"},"stoppedBeforeStart":{"type":"boolean","description":"The app reported stopped before the start was issued; false when the wait ran out"},"waitedSec":{"type":"integer","description":"Seconds spent waiting for the stop"}},"required":["id","targetState","stoppedBeforeStart","waitedSec"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Read a page of container logs for an app. Paging starts at the newest lines and moves back in time by default; inside a page, chunks and lines run oldest to newest, so the last line is the most recent. Lines are the app's own output, as it wrote them, so they can hold anything it prints, sensitive data included.

> Requires \`monitoring:read\` (Read usage metrics, app logs, build logs and server logs).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"AppLogs":{"type":"object","properties":{"logs":{"type":"array","items":{"type":"object","properties":{"node":{"type":"string","description":"Server address the lines came from"},"from":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"to":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"lines":{"type":"array","items":{"type":"string"}}},"required":["node","from","to","lines"],"additionalProperties":false},"description":"Chunks in chronological order, consecutive lines from one server merged into a chunk that from and to span; the last line of the last chunk is the newest"},"nextCursorNs":{"description":"Pass as cursorNs to fetch the next page in the same direction; null at the end","type":["string","null"]},"hasMore":{"type":"boolean"},"totalLines":{"type":"integer","description":"Lines in this page across every chunk"}},"required":["logs","nextCursorNs","hasMore","totalLines"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/logs":{"get":{"operationId":"apps.logs","summary":"Read a page of container logs for an app. Paging starts at the newest lines and moves back in time by default; inside a page, chunks and lines run oldest to newest, so the last line is the most recent. Lines are the app's own output, as it wrote them, so they can hold anything it prints, sensitive data included.","description":"Requires `monitoring:read` (Read usage metrics, app logs, build logs and server logs).","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"nodes","in":"query","required":false,"description":"Server addresses to read from; omit for every server the app ran on","schema":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1}},"style":"form","explode":true},{"name":"cursorNs","in":"query","required":false,"description":"Cursor from a previous page (nextCursorNs)","schema":{"type":"string"}},{"name":"cursor","in":"query","required":false,"description":"Unix time in milliseconds to seek to when no cursorNs is given; 0 or omitted means now","schema":{"type":"integer","minimum":0}},{"name":"pageSize","in":"query","required":false,"description":"Lines per page, at most 1000 (default 200)","schema":{"type":"integer","minimum":1,"maximum":1000}},{"name":"direction","in":"query","required":false,"description":"Which way the next page moves from the cursor: older (the default) or newer. Lines inside a page are always chronological","schema":{"type":"string","enum":["older","newer"]}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppLogs"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Resource usage of an app over a time range, bucketed per server

> Requires \`monitoring:read\` (Read usage metrics, app logs, build logs and server logs).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"AppUsage":{"type":"object","properties":{"start":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"end":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"intervalSeconds":{"type":"integer","description":"Width of each bucket"},"series":{"type":"array","items":{"type":"object","properties":{"node":{"type":"string","description":"Server address"},"points":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time","description":"Bucket start"},"cpuNs":{"type":"number","description":"CPU time consumed, in nanoseconds"},"gpuNs":{"type":"number","description":"GPU time consumed, in nanoseconds"},"gpuMemBytesSecs":{"type":"number","description":"GPU memory held, in byte-seconds"},"memBytesSecs":{"type":"number","description":"RAM held, in byte-seconds"},"storageBytesSecs":{"type":"number","description":"Disk held, in byte-seconds"},"logStorageBytesSecs":{"type":"number","description":"Log storage held, in byte-seconds"},"bwInBytes":{"type":"number","description":"Bytes received"},"bwOutBytes":{"type":"number","description":"Bytes sent"}},"required":["at","cpuNs","gpuNs","gpuMemBytesSecs","memBytesSecs","storageBytesSecs","logStorageBytesSecs","bwInBytes","bwOutBytes"],"additionalProperties":false}}},"required":["node","points"],"additionalProperties":false},"description":"One series per server the app ran on; each point sums the bucket"}},"required":["start","end","intervalSeconds","series"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/usage":{"get":{"operationId":"apps.usage","summary":"Resource usage of an app over a time range, bucketed per server","description":"Requires `monitoring:read` (Read usage metrics, app logs, build logs and server logs).","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"start","in":"query","required":false,"description":"Range start (ISO 8601); defaults to one hour before end","schema":{"type":"string","format":"date-time"}},{"name":"end","in":"query","required":false,"description":"Range end (ISO 8601); defaults to now. At most 30 days after start","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppUsage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Move an app to another project of its organization. Admins may, and so may the app's creator. Nothing about the app itself changes.

> Requires \`apps:admin\` (Delete or move apps and manage who they are shared with).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"App":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt"],"additionalProperties":false},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/move":{"post":{"operationId":"apps.move","summary":"Move an app to another project of its organization. Admins may, and so may the app's creator. Nothing about the app itself changes.","description":"Requires `apps:admin` (Delete or move apps and manage who they are shared with).","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"targetProjectId":{"type":"string","pattern":"^[0-9a-fA-F]{24}$","description":"Project to move the app into"}},"required":["targetProjectId"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/App"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## How long the app's container logs are kept

> Requires \`apps:read\` (View apps, their configuration and status).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"LogRetention":{"type":"object","properties":{"days":{"type":"integer","description":"How many days of container logs are kept"},"isDefault":{"type":"boolean","description":"The platform default applies; no per-app setting"}},"required":["days","isDefault"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/logs/retention":{"get":{"operationId":"apps.logs.retention.get","summary":"How long the app's container logs are kept","description":"Requires `apps:read` (View apps, their configuration and status).","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogRetention"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Set how many days the app's container logs are kept. 1 to 180; 0 goes back to the platform default

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps).\
> \
> Errors specific to this operation:\
> \- 429 \`too\_many\_attempts\`: More than three log changes in a minute

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"LogRetention":{"type":"object","properties":{"days":{"type":"integer","description":"How many days of container logs are kept"},"isDefault":{"type":"boolean","description":"The platform default applies; no per-app setting"}},"required":["days","isDefault"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/logs/retention":{"patch":{"operationId":"apps.logs.retention.update","summary":"Set how many days the app's container logs are kept. 1 to 180; 0 goes back to the platform default","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps).\n\nErrors specific to this operation:\n- 429 `too_many_attempts`: More than three log changes in a minute","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"integer","minimum":0,"maximum":180,"description":"Days to keep; 0 for the default"}},"required":["days"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogRetention"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Delete the app's container logs older than a point in time. Cannot be undone.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps).\
> \
> Errors specific to this operation:\
> \- 429 \`too\_many\_attempts\`: More than three log changes in a minute

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}/apps/{appId}/logs/clear":{"post":{"operationId":"apps.logs.clear","summary":"Delete the app's container logs older than a point in time. Cannot be undone.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps).\n\nErrors specific to this operation:\n- 429 `too_many_attempts`: More than three log changes in a minute","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"olderThan":{"type":"string","format":"date-time","description":"Lines before this instant are deleted"}},"required":["olderThan"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"deletedCount":{"type":"integer","description":"Lines deleted"}},"required":["deletedCount"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## The app's container logs as one text, oldest first. Every line is stamped with its time and server, up to maxBytes (1 MiB by default, 4 MiB at most). For a page at a time use apps.logs instead. Lines are the app's own output, as it wrote them, so they can hold anything it prints, sensitive data included.

> Requires \`monitoring:read\` (Read usage metrics, app logs, build logs and server logs).\
> \
> Errors specific to this operation:\
> \- 429 \`too\_many\_attempts\`: More than three log changes in a minute

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Apps"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}/apps/{appId}/logs/export":{"get":{"operationId":"apps.logs.export","summary":"The app's container logs as one text, oldest first. Every line is stamped with its time and server, up to maxBytes (1 MiB by default, 4 MiB at most). For a page at a time use apps.logs instead. Lines are the app's own output, as it wrote them, so they can hold anything it prints, sensitive data included.","description":"Requires `monitoring:read` (Read usage metrics, app logs, build logs and server logs).\n\nErrors specific to this operation:\n- 429 `too_many_attempts`: More than three log changes in a minute","tags":["Apps"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"nodes","in":"query","required":false,"description":"Server addresses to include; omit for all","schema":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1}},"style":"form","explode":true},{"name":"maxBytes","in":"query","required":false,"description":"Stop after this many bytes of text; 1048576 by default","schema":{"type":"integer","minimum":1024,"maximum":4194304}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"The log lines, newline-separated"},"lines":{"type":"integer"},"truncated":{"type":"boolean","description":"maxBytes was reached before the end; the newest lines are missing"}},"required":["text","lines","truncated"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Builds

Follow repository builds, read builder logs and trigger a deployment.

Check whether an earlier build image is available with GET on its /rollback path. POST redeploys that image without rebuilding: a running app redeploys, a stopped app uses it at its next start. Rollback needs apps:write and permission to edit, changes only the image in the current configuration, and is refused during an active build or without a deployable image. The next build replaces it.

## List the builds of a GitHub-sourced app, newest first

> Requires \`apps:read\` (View apps, their configuration and status).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Builds"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Build":{"type":"object","properties":{"id":{"type":"string","description":"Build id"},"appId":{"type":"string"},"status":{"type":"string","enum":["pending","running","succeeded","failed","cancelled"],"description":"running until the image is built and the app deployed"},"step":{"type":"string","enum":["vcs_import","image_build","app_deploy","completed"],"description":"Pipeline step in progress, or completed"},"imageBuildStatus":{"description":"The image builder's own status (Created, Running, Completed, Failed, Canceled, TimedOut) once that step started","type":["string","null"]},"source":{"anyOf":[{"type":"object","properties":{"repo":{"type":["string","null"]},"branch":{"type":["string","null"]},"commitSha":{"type":["string","null"]},"commitMessage":{"type":["string","null"]},"contextDir":{"type":["string","null"]},"dockerfilePath":{"type":["string","null"]}},"required":["repo","branch","commitSha","commitMessage","contextDir","dockerfilePath"],"additionalProperties":false},{"type":"null"}],"description":"What was built; the commit is filled in once the build starts"},"error":{"anyOf":[{"type":"object","properties":{"step":{"type":"string","description":"Step the build failed at"},"message":{"type":"string"},"code":{"type":["string","null"]}},"required":["step","message","code"],"additionalProperties":false},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"finishedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the build again; null once it has finished"}},"required":["id","appId","status","step","imageBuildStatus","source","error","createdAt","startedAt","finishedAt","pollAfterSec"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/builds":{"get":{"operationId":"apps.builds.list","summary":"List the builds of a GitHub-sourced app, newest first","description":"Requires `apps:read` (View apps, their configuration and status).","tags":["Builds"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"builds":{"type":"array","items":{"$ref":"#/components/schemas/Build"}},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]}},"required":["builds","nextCursor"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Start a new build of a GitHub-sourced app from its branch head, or from one commit, cancelling any build still running. The app is redeployed and started when the build succeeds; poll apps.builds.get to follow it.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps), \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\
> \
> Errors specific to this operation:\
> \- 409 \`app\_source\_unsupported\`: The app is not built from a repository\
> \- 403 \`serverless\_disabled\`: The app would run on Kinesis serverless capacity, which the organization keeps to its admins; target a grid\
> \- 503 \`github\_not\_configured\`: GitHub deployments are not enabled on this server\
> \- 404 \`github\_installation\_not\_found\`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\
> \- 404 \`github\_repo\_not\_found\`: The installation cannot see the repository; grant the GitHub App access to it\
> \- 502 \`upstream\_github\`: GitHub did not answer; retry shortly\
> \- 404 \`github\_commit\_not\_found\`: No commit has this sha

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Builds"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Build":{"type":"object","properties":{"id":{"type":"string","description":"Build id"},"appId":{"type":"string"},"status":{"type":"string","enum":["pending","running","succeeded","failed","cancelled"],"description":"running until the image is built and the app deployed"},"step":{"type":"string","enum":["vcs_import","image_build","app_deploy","completed"],"description":"Pipeline step in progress, or completed"},"imageBuildStatus":{"description":"The image builder's own status (Created, Running, Completed, Failed, Canceled, TimedOut) once that step started","type":["string","null"]},"source":{"anyOf":[{"type":"object","properties":{"repo":{"type":["string","null"]},"branch":{"type":["string","null"]},"commitSha":{"type":["string","null"]},"commitMessage":{"type":["string","null"]},"contextDir":{"type":["string","null"]},"dockerfilePath":{"type":["string","null"]}},"required":["repo","branch","commitSha","commitMessage","contextDir","dockerfilePath"],"additionalProperties":false},{"type":"null"}],"description":"What was built; the commit is filled in once the build starts"},"error":{"anyOf":[{"type":"object","properties":{"step":{"type":"string","description":"Step the build failed at"},"message":{"type":"string"},"code":{"type":["string","null"]}},"required":["step","message","code"],"additionalProperties":false},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"finishedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the build again; null once it has finished"}},"required":["id","appId","status","step","imageBuildStatus","source","error","createdAt","startedAt","finishedAt","pollAfterSec"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/builds":{"post":{"operationId":"apps.builds.create","summary":"Start a new build of a GitHub-sourced app from its branch head, or from one commit, cancelling any build still running. The app is redeployed and started when the build succeeds; poll apps.builds.get to follow it.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps), `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\n\nErrors specific to this operation:\n- 409 `app_source_unsupported`: The app is not built from a repository\n- 403 `serverless_disabled`: The app would run on Kinesis serverless capacity, which the organization keeps to its admins; target a grid\n- 503 `github_not_configured`: GitHub deployments are not enabled on this server\n- 404 `github_installation_not_found`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\n- 404 `github_repo_not_found`: The installation cannot see the repository; grant the GitHub App access to it\n- 502 `upstream_github`: GitHub did not answer; retry shortly\n- 404 `github_commit_not_found`: No commit has this sha","tags":["Builds"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"commitSha":{"description":"Commit to build instead of the branch head","type":"string","pattern":"^[0-9a-fA-F]{7,40}$"}}}}}},"responses":{"202":{"description":"Accepted","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"build":{"$ref":"#/components/schemas/Build"},"cancelledBuilds":{"type":"integer","description":"Builds that were still running and were cancelled"}},"required":["build","cancelledBuilds"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Get one build: its status, pipeline step, source commit and error

> Requires \`apps:read\` (View apps, their configuration and status).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Builds"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Build":{"type":"object","properties":{"id":{"type":"string","description":"Build id"},"appId":{"type":"string"},"status":{"type":"string","enum":["pending","running","succeeded","failed","cancelled"],"description":"running until the image is built and the app deployed"},"step":{"type":"string","enum":["vcs_import","image_build","app_deploy","completed"],"description":"Pipeline step in progress, or completed"},"imageBuildStatus":{"description":"The image builder's own status (Created, Running, Completed, Failed, Canceled, TimedOut) once that step started","type":["string","null"]},"source":{"anyOf":[{"type":"object","properties":{"repo":{"type":["string","null"]},"branch":{"type":["string","null"]},"commitSha":{"type":["string","null"]},"commitMessage":{"type":["string","null"]},"contextDir":{"type":["string","null"]},"dockerfilePath":{"type":["string","null"]}},"required":["repo","branch","commitSha","commitMessage","contextDir","dockerfilePath"],"additionalProperties":false},{"type":"null"}],"description":"What was built; the commit is filled in once the build starts"},"error":{"anyOf":[{"type":"object","properties":{"step":{"type":"string","description":"Step the build failed at"},"message":{"type":"string"},"code":{"type":["string","null"]}},"required":["step","message","code"],"additionalProperties":false},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"finishedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the build again; null once it has finished"}},"required":["id","appId","status","step","imageBuildStatus","source","error","createdAt","startedAt","finishedAt","pollAfterSec"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/builds/{buildId}":{"get":{"operationId":"apps.builds.get","summary":"Get one build: its status, pipeline step, source commit and error","description":"Requires `apps:read` (View apps, their configuration and status).","tags":["Builds"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"buildId","in":"path","required":true,"description":"Build id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Build"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Read the image builder's log for a build. The whole log once the build has finished, a snapshot of what has been written so far while it runs (empty until the builder starts writing). It includes whatever the repository's build steps print, sensitive data included.

> Requires \`monitoring:read\` (Read usage metrics, app logs, build logs and server logs).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Builds"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"BuildLogs":{"type":"object","properties":{"id":{"type":"string","description":"Build id"},"status":{"type":"string","enum":["pending","running","succeeded","failed","cancelled"]},"imageBuildStatus":{"type":["string","null"]},"lines":{"type":"array","items":{"type":"string"},"description":"Builder output, oldest first"},"complete":{"type":"boolean","description":"The image build has finished, so the log is final"},"truncated":{"type":"boolean","description":"Older lines were dropped to keep the last 2000"}},"required":["id","status","imageBuildStatus","lines","complete","truncated"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/builds/{buildId}/logs":{"get":{"operationId":"apps.builds.logs","summary":"Read the image builder's log for a build. The whole log once the build has finished, a snapshot of what has been written so far while it runs (empty until the builder starts writing). It includes whatever the repository's build steps print, sensitive data included.","description":"Requires `monitoring:read` (Read usage metrics, app logs, build logs and server logs).","tags":["Builds"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"buildId","in":"path","required":true,"description":"Build id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuildLogs"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Whether an earlier build's image can still be redeployed with rollback\_build. False when the build has no image (it failed or was cancelled) or its image is no longer stored.

> Requires \`apps:read\` (View apps, their configuration and status).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Builds"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}/apps/{appId}/builds/{buildId}/rollback":{"get":{"operationId":"apps.builds.rollbackCheck","summary":"Whether an earlier build's image can still be redeployed with rollback_build. False when the build has no image (it failed or was cancelled) or its image is no longer stored.","description":"Requires `apps:read` (View apps, their configuration and status).","tags":["Builds"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"buildId","in":"path","required":true,"description":"Build id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"imageAvailable":{"type":"boolean","description":"The build's image can be redeployed"}},"required":["imageAvailable"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Redeploy the image of an earlier build without rebuilding it. A running app is redeployed with that image; a stopped app uses it at its next start. The next build (a push with autoDeploy on, or a triggered build) replaces it again.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps).\
> \
> Errors specific to this operation:\
> \- 409 \`app\_source\_unsupported\`: The app is not built from a repository\
> \- 409 \`build\_not\_deployable\`: Nothing to roll back to: the build has no image (it failed or was cancelled before its image was built, or the image is gone), or the app has never been deployed (start a new build instead)\
> \- 409 \`build\_in\_progress\`: A build is running and deploys when it finishes

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Builds"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Build":{"type":"object","properties":{"id":{"type":"string","description":"Build id"},"appId":{"type":"string"},"status":{"type":"string","enum":["pending","running","succeeded","failed","cancelled"],"description":"running until the image is built and the app deployed"},"step":{"type":"string","enum":["vcs_import","image_build","app_deploy","completed"],"description":"Pipeline step in progress, or completed"},"imageBuildStatus":{"description":"The image builder's own status (Created, Running, Completed, Failed, Canceled, TimedOut) once that step started","type":["string","null"]},"source":{"anyOf":[{"type":"object","properties":{"repo":{"type":["string","null"]},"branch":{"type":["string","null"]},"commitSha":{"type":["string","null"]},"commitMessage":{"type":["string","null"]},"contextDir":{"type":["string","null"]},"dockerfilePath":{"type":["string","null"]}},"required":["repo","branch","commitSha","commitMessage","contextDir","dockerfilePath"],"additionalProperties":false},{"type":"null"}],"description":"What was built; the commit is filled in once the build starts"},"error":{"anyOf":[{"type":"object","properties":{"step":{"type":"string","description":"Step the build failed at"},"message":{"type":"string"},"code":{"type":["string","null"]}},"required":["step","message","code"],"additionalProperties":false},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"finishedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the build again; null once it has finished"}},"required":["id","appId","status","step","imageBuildStatus","source","error","createdAt","startedAt","finishedAt","pollAfterSec"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/builds/{buildId}/rollback":{"post":{"operationId":"apps.builds.rollback","summary":"Redeploy the image of an earlier build without rebuilding it. A running app is redeployed with that image; a stopped app uses it at its next start. The next build (a push with autoDeploy on, or a triggered build) replaces it again.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps).\n\nErrors specific to this operation:\n- 409 `app_source_unsupported`: The app is not built from a repository\n- 409 `build_not_deployable`: Nothing to roll back to: the build has no image (it failed or was cancelled before its image was built, or the image is gone), or the app has never been deployed (start a new build instead)\n- 409 `build_in_progress`: A build is running and deploys when it finishes","tags":["Builds"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"buildId","in":"path","required":true,"description":"Build id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"build":{"description":"The build whose image is now deployed","$ref":"#/components/schemas/Build"},"redeploying":{"type":"boolean","description":"The app is running and is being redeployed with the image"}},"required":["build","redeploying"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Domains

Reserve hostnames, verify DNS and attach custom domains.

## Reserve a custom domain for an app and get the DNS record the user must create at their DNS provider. ready is true once the domain is verified and holds a current certificate, so apps.domains.add can attach it. Members of the project's organization only.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Domains"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"DnsRecord":{"type":"object","properties":{"type":{"type":"string","description":"Record type, e.g. CNAME or NS"},"name":{"type":"string","description":"Record name to create"},"value":{"type":"string","description":"Record value"}},"required":["type","name","value"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/projects/{projectId}/apps/{appId}/domains/reserve":{"post":{"operationId":"apps.domains.reserve","summary":"Reserve a custom domain for an app and get the DNS record the user must create at their DNS provider. ready is true once the domain is verified and holds a current certificate, so apps.domains.add can attach it. Members of the project's organization only.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps).","tags":["Domains"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"domain":{"type":"string","pattern":"^(?=.{4,253}$)(?!-)[a-z0-9-]{1,63}(?<!-)(\\.(?!-)[a-z0-9-]{1,63}(?<!-))+$","description":"Fully qualified domain name, e.g. app.example.com"}},"required":["domain"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"domain":{"type":"string"},"ready":{"type":"boolean","description":"Verified with an unexpired certificate; add attaches it without further DNS steps"},"dnsRecord":{"anyOf":[{"$ref":"#/components/schemas/DnsRecord"},{"type":"null"}],"description":"The record to create; null once no delegation is needed"},"certificate":{"anyOf":[{"type":"object","properties":{"notBefore":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"notAfter":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["notBefore","notAfter"],"additionalProperties":false},{"type":"null"}],"description":"Validity of the issued certificate, when one exists"}},"required":["domain","ready","dnsRecord","certificate"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Attach a custom domain to an app: verifies the DNS record from reserve, issues the certificate and redeploys the app with the domain. Until the record resolves this fails with domain\_not\_verified, carrying the record to create; retry after the user has created it. Attaching an already attached domain changes nothing. Members of the project's organization only.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps).\
> \
> Errors specific to this operation:\
> \- 409 \`domain\_not\_verified\`: The DNS record is not in place yet; dnsRecord says what to create, retry once it resolves\
> \- 400 \`grid\_to\_serverless\`: A grid app cannot move back to serverless\
> \- 400 \`no\_suitable\_machines\`: No machine can run the app's configuration\
> \- 400 \`serverless\_gpu\_unavailable\`: A serverless app saved with a GPU before placement went CPU-only; move it to a grid first\
> \- 429 \`grid\_quota\_exceeded\`: The grids the app targets have no room for it under their per-person app and instance quotas

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Domains"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}/apps/{appId}/domains":{"post":{"operationId":"apps.domains.add","summary":"Attach a custom domain to an app: verifies the DNS record from reserve, issues the certificate and redeploys the app with the domain. Until the record resolves this fails with domain_not_verified, carrying the record to create; retry after the user has created it. Attaching an already attached domain changes nothing. Members of the project's organization only.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps).\n\nErrors specific to this operation:\n- 409 `domain_not_verified`: The DNS record is not in place yet; dnsRecord says what to create, retry once it resolves\n- 400 `grid_to_serverless`: A grid app cannot move back to serverless\n- 400 `no_suitable_machines`: No machine can run the app's configuration\n- 400 `serverless_gpu_unavailable`: A serverless app saved with a GPU before placement went CPU-only; move it to a grid first\n- 429 `grid_quota_exceeded`: The grids the app targets have no room for it under their per-person app and instance quotas","tags":["Domains"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"domain":{"type":"string","pattern":"^(?=.{4,253}$)(?!-)[a-z0-9-]{1,63}(?<!-)(\\.(?!-)[a-z0-9-]{1,63}(?<!-))+$","description":"Fully qualified domain name, e.g. app.example.com"}},"required":["domain"]}}}},"responses":{"201":{"description":"Created","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"domain":{"type":"string"},"domains":{"type":"array","items":{"type":"string"},"description":"Custom domains attached to the app"},"defaultDomain":{"description":"Domain the dashboard shows as the app's address; null for the Kinesis-issued one","type":["string","null"]}},"required":["domain","domains","defaultDomain"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Detach a custom domain from an app and redeploy it without the domain; a default domain that is removed falls back to the Kinesis-issued one. Members of the project's organization only.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps).\
> \
> Errors specific to this operation:\
> \- 400 \`grid\_to\_serverless\`: A grid app cannot move back to serverless\
> \- 400 \`no\_suitable\_machines\`: No machine can run the app's configuration\
> \- 400 \`serverless\_gpu\_unavailable\`: A serverless app saved with a GPU before placement went CPU-only; move it to a grid first\
> \- 429 \`grid\_quota\_exceeded\`: The grids the app targets have no room for it under their per-person app and instance quotas

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Domains"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}/apps/{appId}/domains/{domain}":{"delete":{"operationId":"apps.domains.remove","summary":"Detach a custom domain from an app and redeploy it without the domain; a default domain that is removed falls back to the Kinesis-issued one. Members of the project's organization only.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps).\n\nErrors specific to this operation:\n- 400 `grid_to_serverless`: A grid app cannot move back to serverless\n- 400 `no_suitable_machines`: No machine can run the app's configuration\n- 400 `serverless_gpu_unavailable`: A serverless app saved with a GPU before placement went CPU-only; move it to a grid first\n- 429 `grid_quota_exceeded`: The grids the app targets have no room for it under their per-person app and instance quotas","tags":["Domains"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"domain","in":"path","required":true,"description":"Fully qualified domain name, e.g. app.example.com","schema":{"type":"string","pattern":"^(?=.{4,253}$)(?!-)[a-z0-9-]{1,63}(?<!-)(\\.(?!-)[a-z0-9-]{1,63}(?<!-))+$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"domain":{"type":"string"},"domains":{"type":"array","items":{"type":"string"},"description":"Custom domains attached to the app"},"defaultDomain":{"description":"Domain the dashboard shows as the app's address; null for the Kinesis-issued one","type":["string","null"]},"deleted":{"type":"boolean","const":true}},"required":["domain","domains","defaultDomain","deleted"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Choose which domain the dashboard shows as the app's address: an attached custom domain, or null for the Kinesis-issued hostname.

> Requires \`apps:write\` (Create, update, start, stop, restart and rebuild apps).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Domains"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/projects/{projectId}/apps/{appId}/domains/default":{"post":{"operationId":"apps.domains.setDefault","summary":"Choose which domain the dashboard shows as the app's address: an attached custom domain, or null for the Kinesis-issued hostname.","description":"Requires `apps:write` (Create, update, start, stop, restart and rebuild apps).","tags":["Domains"],"parameters":[{"name":"projectId","in":"path","required":true,"description":"Project id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"path","required":true,"description":"App id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"domain":{"anyOf":[{"type":"string","pattern":"^(?=.{4,253}$)(?!-)[a-z0-9-]{1,63}(?<!-)(\\.(?!-)[a-z0-9-]{1,63}(?<!-))+$","description":"Fully qualified domain name, e.g. app.example.com"},{"type":"null"}],"description":"An attached custom domain, or null for the Kinesis-issued hostname"}},"required":["domain"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"domains":{"type":"array","items":{"type":"string"},"description":"Custom domains attached to the app"},"defaultDomain":{"description":"Domain the dashboard shows as the app's address; null for the Kinesis-issued one","type":["string","null"]}},"required":["domains","defaultDomain"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# GitHub

Browse connected GitHub accounts, repositories and source files.

## List the GitHub accounts and organizations the caller connected through the Kinesis GitHub App. Connecting one is a browser step (connectUrl): open a project, New app, Source: GitHub.

> Requires \`vcs:read\` (Browse connected GitHub accounts, their repositories, branches and commits).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"GitHub"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"GitHubAccount":{"type":"object","properties":{"installationId":{"type":"integer","description":"Installation id to pass to the other GitHub operations"},"login":{"type":"string","description":"GitHub user or organization name"},"type":{"type":"string","enum":["user","organization"]},"avatarUrl":{"type":["string","null"]}},"required":["installationId","login","type","avatarUrl"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/vcs/github/accounts":{"get":{"operationId":"vcs.github.accounts","summary":"List the GitHub accounts and organizations the caller connected through the Kinesis GitHub App. Connecting one is a browser step (connectUrl): open a project, New app, Source: GitHub.","description":"Requires `vcs:read` (Browse connected GitHub accounts, their repositories, branches and commits).","tags":["GitHub"],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"GitHub deployments are enabled on this server"},"accounts":{"type":"array","items":{"$ref":"#/components/schemas/GitHubAccount"}},"connectUrl":{"type":"string","description":"Dashboard page where the user connects a GitHub account"}},"required":["enabled","accounts","connectUrl"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## List the repositories a connected GitHub installation can read, alphabetically

> Requires \`vcs:read\` (Browse connected GitHub accounts, their repositories, branches and commits).\
> \
> Errors specific to this operation:\
> \- 503 \`github\_not\_configured\`: GitHub deployments are not enabled on this server\
> \- 404 \`github\_installation\_not\_found\`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\
> \- 404 \`github\_repo\_not\_found\`: The installation cannot see the repository; grant the GitHub App access to it\
> \- 502 \`upstream\_github\`: GitHub did not answer; retry shortly

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"GitHub"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"GitHubRepo":{"type":"object","properties":{"id":{"type":"integer","description":"GitHub's numeric repository id"},"name":{"type":"string","description":"owner/name"},"defaultBranch":{"type":"string"},"private":{"type":"boolean"},"url":{"type":"string"},"description":{"type":["string","null"]},"language":{"type":["string","null"]},"updatedAt":{"type":"string","description":"Last push or change, ISO 8601"}},"required":["id","name","defaultBranch","private","url","description","language","updatedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/vcs/github/repos":{"get":{"operationId":"vcs.github.repos","summary":"List the repositories a connected GitHub installation can read, alphabetically","description":"Requires `vcs:read` (Browse connected GitHub accounts, their repositories, branches and commits).\n\nErrors specific to this operation:\n- 503 `github_not_configured`: GitHub deployments are not enabled on this server\n- 404 `github_installation_not_found`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\n- 404 `github_repo_not_found`: The installation cannot see the repository; grant the GitHub App access to it\n- 502 `upstream_github`: GitHub did not answer; retry shortly","tags":["GitHub"],"parameters":[{"name":"installationId","in":"query","required":true,"description":"GitHub App installation id, from the connected accounts list","schema":{"type":"integer","exclusiveMinimum":0}},{"name":"search","in":"query","required":false,"description":"Case-insensitive substring of owner/name","schema":{"type":"string","minLength":1,"maxLength":100}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"$ref":"#/components/schemas/GitHubRepo"}},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]}},"required":["repos","nextCursor"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## List the branches of a repository with the sha each points at

> Requires \`vcs:read\` (Browse connected GitHub accounts, their repositories, branches and commits).\
> \
> Errors specific to this operation:\
> \- 503 \`github\_not\_configured\`: GitHub deployments are not enabled on this server\
> \- 404 \`github\_installation\_not\_found\`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\
> \- 404 \`github\_repo\_not\_found\`: The installation cannot see the repository; grant the GitHub App access to it\
> \- 502 \`upstream\_github\`: GitHub did not answer; retry shortly

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"GitHub"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/vcs/github/branches":{"get":{"operationId":"vcs.github.branches","summary":"List the branches of a repository with the sha each points at","description":"Requires `vcs:read` (Browse connected GitHub accounts, their repositories, branches and commits).\n\nErrors specific to this operation:\n- 503 `github_not_configured`: GitHub deployments are not enabled on this server\n- 404 `github_installation_not_found`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\n- 404 `github_repo_not_found`: The installation cannot see the repository; grant the GitHub App access to it\n- 502 `upstream_github`: GitHub did not answer; retry shortly","tags":["GitHub"],"parameters":[{"name":"installationId","in":"query","required":true,"description":"GitHub App installation id, from the connected accounts list","schema":{"type":"integer","exclusiveMinimum":0}},{"name":"repo","in":"query","required":true,"description":"Repository as owner/name","schema":{"type":"string","pattern":"^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9._-]{1,100}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"defaultBranch":{"type":"string"},"branches":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"sha":{"type":"string"},"protected":{"type":"boolean"}},"required":["name","sha","protected"],"additionalProperties":false}}},"required":["defaultBranch","branches"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## List the newest commits of a branch, or look up one commit by sha. The sha of a commit can be passed to apps.builds.create to build that commit rather than the branch head.

> Requires \`vcs:read\` (Browse connected GitHub accounts, their repositories, branches and commits).\
> \
> Errors specific to this operation:\
> \- 503 \`github\_not\_configured\`: GitHub deployments are not enabled on this server\
> \- 404 \`github\_installation\_not\_found\`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\
> \- 404 \`github\_repo\_not\_found\`: The installation cannot see the repository; grant the GitHub App access to it\
> \- 502 \`upstream\_github\`: GitHub did not answer; retry shortly\
> \- 404 \`github\_branch\_not\_found\`: The repository has no such branch\
> \- 404 \`github\_commit\_not\_found\`: No commit has this sha

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"GitHub"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"GitHubCommit":{"type":"object","properties":{"sha":{"type":"string"},"message":{"type":"string","description":"First line of the commit message"},"author":{"type":"string","description":"GitHub login of the author; empty when unknown"},"committedAt":{"type":"string","description":"ISO 8601; empty when unknown"}},"required":["sha","message","author","committedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/vcs/github/commits":{"get":{"operationId":"vcs.github.commits","summary":"List the newest commits of a branch, or look up one commit by sha. The sha of a commit can be passed to apps.builds.create to build that commit rather than the branch head.","description":"Requires `vcs:read` (Browse connected GitHub accounts, their repositories, branches and commits).\n\nErrors specific to this operation:\n- 503 `github_not_configured`: GitHub deployments are not enabled on this server\n- 404 `github_installation_not_found`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\n- 404 `github_repo_not_found`: The installation cannot see the repository; grant the GitHub App access to it\n- 502 `upstream_github`: GitHub did not answer; retry shortly\n- 404 `github_branch_not_found`: The repository has no such branch\n- 404 `github_commit_not_found`: No commit has this sha","tags":["GitHub"],"parameters":[{"name":"installationId","in":"query","required":true,"description":"GitHub App installation id, from the connected accounts list","schema":{"type":"integer","exclusiveMinimum":0}},{"name":"repo","in":"query","required":true,"description":"Repository as owner/name","schema":{"type":"string","pattern":"^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9._-]{1,100}$"}},{"name":"branch","in":"query","required":false,"description":"Branch to list; default: the repository's default branch","schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"sha","in":"query","required":false,"description":"Look up this one commit instead of listing a branch","schema":{"type":"string","pattern":"^[0-9a-fA-F]{7,40}$"}},{"name":"limit","in":"query","required":false,"description":"Commits to return, 1 to 100 (default 20)","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"branch":{"description":"Branch listed; null for a sha lookup","type":["string","null"]},"commits":{"type":"array","items":{"$ref":"#/components/schemas/GitHubCommit"},"description":"Newest first"}},"required":["branch","commits"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Find the Dockerfiles in a repository and check that contextDir plus dockerfilePath names one; for the resolved file, report the ports it exposes and its CMD and ENTRYPOINT. A repository without a Dockerfile can still be deployed: write one and pass it as source.dockerfileContent to apps.create.

> Requires \`vcs:read\` (Browse connected GitHub accounts, their repositories, branches and commits).\
> \
> Errors specific to this operation:\
> \- 503 \`github\_not\_configured\`: GitHub deployments are not enabled on this server\
> \- 404 \`github\_installation\_not\_found\`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\
> \- 404 \`github\_repo\_not\_found\`: The installation cannot see the repository; grant the GitHub App access to it\
> \- 502 \`upstream\_github\`: GitHub did not answer; retry shortly\
> \- 404 \`github\_branch\_not\_found\`: The repository has no such branch

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"GitHub"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/vcs/github/dockerfiles":{"get":{"operationId":"vcs.github.dockerfiles","summary":"Find the Dockerfiles in a repository and check that contextDir plus dockerfilePath names one; for the resolved file, report the ports it exposes and its CMD and ENTRYPOINT. A repository without a Dockerfile can still be deployed: write one and pass it as source.dockerfileContent to apps.create.","description":"Requires `vcs:read` (Browse connected GitHub accounts, their repositories, branches and commits).\n\nErrors specific to this operation:\n- 503 `github_not_configured`: GitHub deployments are not enabled on this server\n- 404 `github_installation_not_found`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\n- 404 `github_repo_not_found`: The installation cannot see the repository; grant the GitHub App access to it\n- 502 `upstream_github`: GitHub did not answer; retry shortly\n- 404 `github_branch_not_found`: The repository has no such branch","tags":["GitHub"],"parameters":[{"name":"installationId","in":"query","required":true,"description":"GitHub App installation id, from the connected accounts list","schema":{"type":"integer","exclusiveMinimum":0}},{"name":"repo","in":"query","required":true,"description":"Repository as owner/name","schema":{"type":"string","pattern":"^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9._-]{1,100}$"}},{"name":"branch","in":"query","required":false,"description":"Branch to inspect; default: the repository's default branch","schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"contextDir","in":"query","required":false,"description":"Build context directory (default /)","schema":{"type":"string","minLength":1,"maxLength":512}},{"name":"dockerfilePath","in":"query","required":false,"description":"Dockerfile path relative to contextDir (default Dockerfile)","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"dockerfiles":{"type":"array","items":{"type":"string"},"description":"Every Dockerfile path found, up to five directories deep; empty when there is none to build from"},"resolvedPathExists":{"type":"boolean","description":"contextDir plus dockerfilePath names an existing file"},"truncated":{"type":"boolean","description":"The repository tree was too large to list completely"},"exposedPorts":{"type":"array","items":{"type":"object","properties":{"port":{"type":"integer"},"protocol":{"type":"string","enum":["tcp","udp"]}},"required":["port","protocol"],"additionalProperties":false},"description":"EXPOSE lines of the resolved Dockerfile"},"command":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"CMD of the resolved Dockerfile"},"entrypoint":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"ENTRYPOINT of the resolved Dockerfile"}},"required":["dockerfiles","resolvedPathExists","truncated","exposedPorts","command","entrypoint"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Read a directory listing or a file of a repository, at the root or a path, on a branch. A text file comes back as content (up to maxBytes, then truncated); a binary file, or one over 1 MB, comes back without it. The way to see what a repository is built from (package.json, requirements.txt, ...) before writing its Dockerfile.

> Requires \`vcs:read\` (Browse connected GitHub accounts, their repositories, branches and commits).\
> \
> Errors specific to this operation:\
> \- 503 \`github\_not\_configured\`: GitHub deployments are not enabled on this server\
> \- 404 \`github\_installation\_not\_found\`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\
> \- 404 \`github\_repo\_not\_found\`: The installation cannot see the repository; grant the GitHub App access to it\
> \- 502 \`upstream\_github\`: GitHub did not answer; retry shortly\
> \- 404 \`github\_path\_not\_found\`: The branch or the path does not exist\
> \- 422 \`github\_file\_too\_large\`: GitHub does not serve files over 100 MB\
> \- 429 \`github\_rate\_limited\`: GitHub is rate limiting the installation; retry after retryAfterSec, when given

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"GitHub"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"GitHubEntry":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string","description":"Relative to the repository root"},"type":{"type":"string","enum":["file","dir","symlink","submodule"]},"size":{"type":"integer","description":"Bytes; 0 for a directory"}},"required":["name","path","type","size"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unprocessable":{"description":"The request is valid but cannot be carried out on this resource (github_file_too_large, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/vcs/github/contents":{"get":{"operationId":"vcs.github.contents","summary":"Read a directory listing or a file of a repository, at the root or a path, on a branch. A text file comes back as content (up to maxBytes, then truncated); a binary file, or one over 1 MB, comes back without it. The way to see what a repository is built from (package.json, requirements.txt, ...) before writing its Dockerfile.","description":"Requires `vcs:read` (Browse connected GitHub accounts, their repositories, branches and commits).\n\nErrors specific to this operation:\n- 503 `github_not_configured`: GitHub deployments are not enabled on this server\n- 404 `github_installation_not_found`: The caller has no connected GitHub account with this installation id; connect it in the dashboard\n- 404 `github_repo_not_found`: The installation cannot see the repository; grant the GitHub App access to it\n- 502 `upstream_github`: GitHub did not answer; retry shortly\n- 404 `github_path_not_found`: The branch or the path does not exist\n- 422 `github_file_too_large`: GitHub does not serve files over 100 MB\n- 429 `github_rate_limited`: GitHub is rate limiting the installation; retry after retryAfterSec, when given","tags":["GitHub"],"parameters":[{"name":"installationId","in":"query","required":true,"description":"GitHub App installation id, from the connected accounts list","schema":{"type":"integer","exclusiveMinimum":0}},{"name":"repo","in":"query","required":true,"description":"Repository as owner/name","schema":{"type":"string","pattern":"^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9._-]{1,100}$"}},{"name":"branch","in":"query","required":false,"description":"Branch to read; default: the repository's default branch","schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"path","in":"query","required":false,"description":"File or directory, relative to the repository root; omit for the root","schema":{"type":"string","minLength":1,"maxLength":512}},{"name":"maxBytes","in":"query","required":false,"description":"Bytes of a file to return, at most 262144 (default 65536)","schema":{"type":"integer","minimum":1,"maximum":262144}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"branch":{"type":"string"},"path":{"type":"string","description":"Path read; empty for the root"},"type":{"type":"string","enum":["dir","file","symlink","submodule"]},"entries":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/GitHubEntry"}},{"type":"null"}],"description":"A directory's entries, directories first, then by name; null for anything else"},"content":{"description":"A text file's content (UTF-8); null for a directory, a binary file or a file over 1 MB","type":["string","null"]},"size":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"A file's size in bytes; null otherwise"},"truncated":{"type":"boolean","description":"A listing stopped at GitHub's 1,000 entries, or content stops at maxBytes, or the file was over 1 MB"},"binary":{"type":"boolean","description":"The file is not text, so content is null"},"target":{"description":"A symlink's target; null otherwise","type":["string","null"]}},"required":["branch","path","type","entries","content","size","truncated","binary","target"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/Unprocessable"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Templates

Find ready-to-deploy configurations from the App Gallery.

GPU templates need a grid: add config.grids.ids before creating the app. Read notes for model placeholders and sizing. The vLLM multi-GPU category is currently hidden from results, although its category enum remains for compatibility.

## List the app gallery's templates as ready-made apps.create configurations (image, ports, env, hardware); pass a template's config to apps.create with a name, edited as needed. notes says what a template needs filled in first.

> Any API key can call this.

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Templates"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"AppTemplate":{"type":"object","properties":{"id":{"type":"string","description":"Template id"},"name":{"type":"string","description":"Suggested app name"},"category":{"type":"string","enum":["llm_general","llm_performance","general"]},"image":{"description":"Container image the template deploys","type":["string","null"]},"paidTierOnly":{"type":"boolean","description":"Meant for organizations with a payment on record"},"notes":{"description":"What to fill in before deploying, when the template is not complete as is","type":["string","null"]},"config":{"type":"object","properties":{"image":{"description":"Container image reference, e.g. nginx:1.27 or ghcr.io/org/app:tag","type":"string","minLength":1,"maxLength":512},"registryAuth":{"description":"Credentials for a private image","type":"object","properties":{"username":{"type":"string","minLength":1,"description":"Registry login"},"password":{"type":"string","minLength":1,"description":"Registry password or token (write-only)"}},"required":["username","password"],"additionalProperties":false},"ports":{"description":"Ports to expose","maxItems":32,"type":"array","items":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535,"description":"Container port"},"protocol":{"type":"string","enum":["tcp","udp","tcp_load_balancer","tcps_load_balancer","http_load_balancer","https_load_balancer","http80_load_balancer","https443_load_balancer","grpcs_load_balancer"],"description":"How the port is exposed"}},"required":["port","protocol"],"additionalProperties":false}},"env":{"description":"Environment variables","maxItems":200,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]*$","description":"Variable name"},"value":{"description":"Value; null keeps the stored value of an existing variable","type":["string","null"]},"secret":{"description":"Store as a secret: never returned by reads","type":"boolean"}},"required":["key","value"],"additionalProperties":false}},"command":{"description":"Container command (CMD) override","maxItems":256,"type":"array","items":{"type":"string"}},"entrypoint":{"description":"Container entrypoint override","maxItems":256,"type":"array","items":{"type":"string"}},"startupCommand":{"description":"Command run inside the container once it starts; null clears it","anyOf":[{"type":"object","properties":{"args":{"minItems":1,"type":"array","items":{"type":"string"},"description":"Command and arguments"},"user":{"description":"User to run as","type":"string"},"timeoutSec":{"description":"Seconds before the command is considered failed","type":"integer","minimum":0},"startPeriodSec":{"description":"Seconds to wait after the container starts","type":"integer","minimum":0}},"required":["args"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"description":"Container health check; null clears it","anyOf":[{"type":"object","properties":{"test":{"minItems":1,"type":"array","items":{"type":"string"},"description":"Command that must exit 0 for the app to count as healthy"},"intervalSec":{"type":"integer","minimum":1,"description":"Seconds between checks"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"description":"Restart behaviour","type":"object","properties":{"mode":{"type":"string","enum":["disabled","always","on_failure","task_completion"],"description":"When to restart a container that exits"},"startTimeoutSec":{"description":"Seconds a start may take before it counts as failed","type":"integer","minimum":0},"maxRetriesPerNode":{"description":"Restarts allowed on one server before moving on","type":"integer","minimum":0},"maxErrorTolerance":{"description":"Failures tolerated before the app is stopped","type":"integer","minimum":0},"numTasks":{"description":"For task_completion: runs to complete before stopping","type":"integer","minimum":0}},"required":["mode"],"additionalProperties":false},"instances":{"description":"Instance count range","type":"object","properties":{"min":{"type":"integer","minimum":0,"description":"Instances to keep running"},"max":{"type":"integer","minimum":0,"description":"Upper bound for scaling"}},"required":["min","max"],"additionalProperties":false},"stateful":{"description":"Keep each instance pinned to its server and disk","type":"boolean"},"hardware":{"description":"Hardware requirements per instance","type":"object","properties":{"gpuCount":{"description":"GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only","type":"integer","minimum":0,"maximum":16},"gpuModels":{"description":"Acceptable GPU models, e.g. H100; empty means any","maxItems":16,"type":"array","items":{"type":"string","minLength":1}},"gpuManufacturer":{"description":"GPU vendor; null means any","anyOf":[{"type":"string","enum":["amd","apple","intel","nvidia","xilinx"]},{"type":"null"}]},"gpuVramBytes":{"description":"Minimum VRAM per GPU, in bytes","type":"integer","minimum":0},"ramBytes":{"description":"RAM per instance, in bytes","type":"integer","exclusiveMinimum":0},"storageBytes":{"description":"Disk per instance, in bytes","type":"integer","exclusiveMinimum":0},"shmemBytes":{"description":"Shared memory (/dev/shm) size, in bytes","type":"integer","minimum":0}},"additionalProperties":false},"placement":{"description":"Where the app may run and who may reach it","type":"object","properties":{"regions":{"description":"Kinesis regions to run in; empty means any","type":"array","items":{"type":"string","enum":["us-west","us-east","us-central","ca-west","ca-east","ca-central","eu-west","eu-north","eu-east","eu-south","eu-central","eu-south-east","ap-east","ap-south-east","ap-north-east","ap-south"]}},"providers":{"description":"Restrict placement to specific providers and regions","type":"array","items":{"type":"object","properties":{"csp":{"type":"string","enum":["aws","azure","gcp","kinesis"],"description":"Cloud provider"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string","minLength":1,"description":"Provider region"},"zones":{"type":"array","items":{"type":"string"},"description":"Zones within the region; empty means any"}},"required":["region","zones"],"additionalProperties":false},"description":"Regions of the provider to allow"}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"description":"Source IPs or CIDR ranges allowed to reach the app; empty means everyone","maxItems":100,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"grids":{"description":"Run on grids instead of serverless capacity (members only, on grids they may deploy to)","type":"object","properties":{"ids":{"description":"Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"priorities":{"description":"Priority per grid id, -100 to 100","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"integer","minimum":-100,"maximum":100}}},"additionalProperties":false},"volumes":{"description":"Persistent volumes","maxItems":4,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$","description":"Volume name, unique within the app"},"target":{"type":"string","pattern":"^\\/.+","description":"Absolute mount path inside the container"},"declaredSizeBytes":{"description":"Expected size, used for placement; 0 or omitted means unspecified","type":"integer","minimum":0,"maximum":17592186044416}},"required":["name","target"],"additionalProperties":false}}},"additionalProperties":false,"description":"Pass as config to apps.create, edited as needed"}},"required":["id","name","category","image","paidTierOnly","notes","config"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/templates":{"get":{"operationId":"templates.list","summary":"List the app gallery's templates as ready-made apps.create configurations (image, ports, env, hardware); pass a template's config to apps.create with a name, edited as needed. notes says what a template needs filled in first.","description":"Any API key can call this.","tags":["Templates"],"parameters":[{"name":"search","in":"query","required":false,"description":"Case-insensitive substring of the name or image","schema":{"type":"string","minLength":1,"maxLength":100}},{"name":"category","in":"query","required":false,"description":"Only templates of this category","schema":{"type":"string","enum":["llm_general","llm_performance","general"]}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"$ref":"#/components/schemas/AppTemplate"}},"galleryUrl":{"type":"string","description":"The dashboard gallery, where the same templates are deployed by hand"}},"required":["templates","galleryUrl"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Sharing

GranteeList.ownerEmail has been removed. Use organization, membership and access fields; resources belong to organizations.

Sharing adds existing members of the resource’s organization. Use POST /sharing/grantees for direct access, and organization invitations for new colleagues. Grant payloads keep editor (Developer) and viewer; membership roles use admin, developer and viewer. Grids accept viewer for use only. Only admins manage grid sharing; admins or creators manage project/app sharing. The older invitation endpoints handle existing invitations only.

## Who a project, app or grid is shared with, and as what

> Any API key can call this.\
> \
> Also requires \`projects:admin\` (Delete projects and manage who they are shared with) when resourceType is project.\
> \
> Also requires \`apps:admin\` (Delete or move apps and manage who they are shared with) when resourceType is app.\
> \
> Also requires \`pools:admin\` (Delete grids and manage who they are shared with) when resourceType is grid.

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Sharing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"GranteeList":{"type":"object","properties":{"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string"},"resourceName":{"type":"string"},"callerRole":{"type":"string","enum":["viewer","editor","owner"],"description":"Legacy permission values: owner means an organization admin; editor means the member who created the resource"},"grantees":{"type":"array","items":{"$ref":"#/components/schemas/Grantee"}}},"required":["resourceType","resourceId","resourceName","callerRole","grantees"],"additionalProperties":false},"Grantee":{"type":"object","properties":{"userId":{"type":"string","description":"User id; what update and revoke take"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string","enum":["viewer","editor"],"description":"Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"},"orgRole":{"anyOf":[{"type":"string","enum":["admin","developer","viewer"]},{"type":"null"}],"description":"Their role in the resource's organization; null for someone outside it"},"grantedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["userId","email","name","role","orgRole","grantedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/sharing/grantees":{"get":{"operationId":"sharing.grantees.list","summary":"Who a project, app or grid is shared with, and as what","description":"Any API key can call this.\n\nAlso requires `projects:admin` (Delete projects and manage who they are shared with) when resourceType is project.\n\nAlso requires `apps:admin` (Delete or move apps and manage who they are shared with) when resourceType is app.\n\nAlso requires `pools:admin` (Delete grids and manage who they are shared with) when resourceType is grid.","tags":["Sharing"],"parameters":[{"name":"resourceType","in":"query","required":true,"description":"What is shared","schema":{"type":"string","enum":["project","app","grid"]}},{"name":"resourceId","in":"query","required":true,"description":"Project or app id, or grid id","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GranteeList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Add members of the resource's organization to a project, app or grid. No invitation: access starts at once. Each role is at most theirs in the organization (a viewer is added as viewer; grids take viewer only), and someone already added gets the new role. People outside the organization can't be added

> Any API key can call this.\
> \
> Also requires \`projects:admin\` (Delete projects and manage who they are shared with) when resourceType is project.\
> \
> Also requires \`apps:admin\` (Delete or move apps and manage who they are shared with) when resourceType is app.\
> \
> Also requires \`pools:admin\` (Delete grids and manage who they are shared with) when resourceType is grid.\
> \
> Errors specific to this operation:\
> \- 400 \`not\_member\`: Someone named isn't in the resource's organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Sharing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"GranteeList":{"type":"object","properties":{"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string"},"resourceName":{"type":"string"},"callerRole":{"type":"string","enum":["viewer","editor","owner"],"description":"Legacy permission values: owner means an organization admin; editor means the member who created the resource"},"grantees":{"type":"array","items":{"$ref":"#/components/schemas/Grantee"}}},"required":["resourceType","resourceId","resourceName","callerRole","grantees"],"additionalProperties":false},"Grantee":{"type":"object","properties":{"userId":{"type":"string","description":"User id; what update and revoke take"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string","enum":["viewer","editor"],"description":"Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"},"orgRole":{"anyOf":[{"type":"string","enum":["admin","developer","viewer"]},{"type":"null"}],"description":"Their role in the resource's organization; null for someone outside it"},"grantedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["userId","email","name","role","orgRole","grantedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/sharing/grantees":{"post":{"operationId":"sharing.grantees.add","summary":"Add members of the resource's organization to a project, app or grid. No invitation: access starts at once. Each role is at most theirs in the organization (a viewer is added as viewer; grids take viewer only), and someone already added gets the new role. People outside the organization can't be added","description":"Any API key can call this.\n\nAlso requires `projects:admin` (Delete projects and manage who they are shared with) when resourceType is project.\n\nAlso requires `apps:admin` (Delete or move apps and manage who they are shared with) when resourceType is app.\n\nAlso requires `pools:admin` (Delete grids and manage who they are shared with) when resourceType is grid.\n\nErrors specific to this operation:\n- 400 `not_member`: Someone named isn't in the resource's organization","tags":["Sharing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string","minLength":1,"description":"Project or app id, or grid id"},"people":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"userId":{"type":"string","pattern":"^[0-9a-fA-F]{24}$","description":"A member's user id"},"role":{"type":"string","enum":["viewer","editor"],"description":"Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"}},"required":["userId","role"]},"description":"Members to add, each with a role"}},"required":["resourceType","resourceId","people"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GranteeList"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Change a grantee's role on a project or app (grids are shared as viewer only). On a grid, taking an old editor grant away also releases the machines and spot groups they added. Not on the caller's own grant.

> Any API key can call this.\
> \
> Also requires \`projects:admin\` (Delete projects and manage who they are shared with) when resourceType is project.\
> \
> Also requires \`apps:admin\` (Delete or move apps and manage who they are shared with) when resourceType is app.\
> \
> Also requires \`pools:admin\` (Delete grids and manage who they are shared with) when resourceType is grid.\
> \
> Errors specific to this operation:\
> \- 403 \`forbidden\`: The grant is the caller's own

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Sharing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Grantee":{"type":"object","properties":{"userId":{"type":"string","description":"User id; what update and revoke take"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string","enum":["viewer","editor"],"description":"Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"},"orgRole":{"anyOf":[{"type":"string","enum":["admin","developer","viewer"]},{"type":"null"}],"description":"Their role in the resource's organization; null for someone outside it"},"grantedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["userId","email","name","role","orgRole","grantedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/sharing/grantees/{granteeUserId}":{"patch":{"operationId":"sharing.grantees.update","summary":"Change a grantee's role on a project or app (grids are shared as viewer only). On a grid, taking an old editor grant away also releases the machines and spot groups they added. Not on the caller's own grant.","description":"Any API key can call this.\n\nAlso requires `projects:admin` (Delete projects and manage who they are shared with) when resourceType is project.\n\nAlso requires `apps:admin` (Delete or move apps and manage who they are shared with) when resourceType is app.\n\nAlso requires `pools:admin` (Delete grids and manage who they are shared with) when resourceType is grid.\n\nErrors specific to this operation:\n- 403 `forbidden`: The grant is the caller's own","tags":["Sharing"],"parameters":[{"name":"granteeUserId","in":"path","required":true,"description":"User id of the grantee, from the grantees list","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string","minLength":1,"description":"Project or app id, or grid id"},"role":{"type":"string","enum":["viewer","editor"],"description":"Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"}},"required":["resourceType","resourceId","role"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string"},"resourceName":{"type":"string"},"callerRole":{"type":"string","enum":["viewer","editor","owner"],"description":"Legacy permission values: owner means an organization admin; editor means the member who created the resource"},"grantees":{"type":"array","items":{"$ref":"#/components/schemas/Grantee"}},"warning":{"description":"Set when a grid could not release the grantee's machines or spot groups; the access change stands","type":["string","null"]}},"required":["resourceType","resourceId","resourceName","callerRole","grantees","warning"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Remove a grantee's access. On a grid, machines and spot groups a former co-manager added leave it too. Not on the caller's own grant (use leave).

> Any API key can call this.\
> \
> Also requires \`projects:admin\` (Delete projects and manage who they are shared with) when resourceType is project.\
> \
> Also requires \`apps:admin\` (Delete or move apps and manage who they are shared with) when resourceType is app.\
> \
> Also requires \`pools:admin\` (Delete grids and manage who they are shared with) when resourceType is grid.\
> \
> Errors specific to this operation:\
> \- 403 \`forbidden\`: The grant is the caller's own

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Sharing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/sharing/grantees/{granteeUserId}":{"delete":{"operationId":"sharing.grantees.revoke","summary":"Remove a grantee's access. On a grid, machines and spot groups a former co-manager added leave it too. Not on the caller's own grant (use leave).","description":"Any API key can call this.\n\nAlso requires `projects:admin` (Delete projects and manage who they are shared with) when resourceType is project.\n\nAlso requires `apps:admin` (Delete or move apps and manage who they are shared with) when resourceType is app.\n\nAlso requires `pools:admin` (Delete grids and manage who they are shared with) when resourceType is grid.\n\nErrors specific to this operation:\n- 403 `forbidden`: The grant is the caller's own","tags":["Sharing"],"parameters":[{"name":"granteeUserId","in":"path","required":true,"description":"User id of the grantee, from the grantees list","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"resourceType","in":"query","required":true,"description":"What is shared","schema":{"type":"string","enum":["project","app","grid"]}},{"name":"resourceId","in":"query","required":true,"description":"Project or app id, or grid id","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"warning":{"description":"Set when a grid could not release the grantee's machines or spot groups; the access change stands","type":["string","null"]}},"required":["warning"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Give up the caller's own access to a shared project, app or grid. On a grid, their machines and spot groups leave with them.

> Requires \`account:write\` (Accept or decline invitations, leave shared resources, manage notifications).\
> \
> Errors specific to this operation:\
> \- 400 \`invalid\_request\`: The caller owns the resource

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Sharing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/sharing/leave":{"post":{"operationId":"sharing.leave","summary":"Give up the caller's own access to a shared project, app or grid. On a grid, their machines and spot groups leave with them.","description":"Requires `account:write` (Accept or decline invitations, leave shared resources, manage notifications).\n\nErrors specific to this operation:\n- 400 `invalid_request`: The caller owns the resource","tags":["Sharing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string","minLength":1,"description":"Project or app id, or grid id"}},"required":["resourceType","resourceId"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"warning":{"description":"Set when a grid could not release the grantee's machines or spot groups; the access change stands","type":["string","null"]}},"required":["warning"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Invitations on a resource the caller manages, or those the caller sent. Pending and accepted ones: by resourceType and resourceId, or the sent ones within the credential's admin scopes and project restrictions

> Any API key can call this.\
> \
> Also requires \`projects:admin\` (Delete projects and manage who they are shared with) when resourceType is project.\
> \
> Also requires \`apps:admin\` (Delete or move apps and manage who they are shared with) when resourceType is app.\
> \
> Also requires \`pools:admin\` (Delete grids and manage who they are shared with) when resourceType is grid.

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Sharing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Invitation":{"type":"object","properties":{"id":{"type":"string","description":"Invitation id"},"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string"},"resourceName":{"type":"string"},"inviteeEmail":{"type":"string"},"invitedByEmail":{"type":"string","description":"Who sent it"},"role":{"type":"string","enum":["viewer","editor"],"description":"Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"},"status":{"type":"string","enum":["pending","accepted","rejected"]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"acceptedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["id","resourceType","resourceId","resourceName","inviteeEmail","invitedByEmail","role","status","createdAt","acceptedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/sharing/invitations":{"get":{"operationId":"sharing.invitations.list","summary":"Invitations on a resource the caller manages, or those the caller sent. Pending and accepted ones: by resourceType and resourceId, or the sent ones within the credential's admin scopes and project restrictions","description":"Any API key can call this.\n\nAlso requires `projects:admin` (Delete projects and manage who they are shared with) when resourceType is project.\n\nAlso requires `apps:admin` (Delete or move apps and manage who they are shared with) when resourceType is app.\n\nAlso requires `pools:admin` (Delete grids and manage who they are shared with) when resourceType is grid.","tags":["Sharing"],"parameters":[{"name":"resourceType","in":"query","required":false,"description":"What is shared","schema":{"type":"string","enum":["project","app","grid"]}},{"name":"resourceId","in":"query","required":false,"description":"With resourceType: the resource whose invitations to list","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"invitations":{"type":"array","items":{"$ref":"#/components/schemas/Invitation"}}},"required":["invitations"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Pending invitations addressed to the caller

> Requires \`account:read\` (View invitations and notifications addressed to the user).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Sharing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Invitation":{"type":"object","properties":{"id":{"type":"string","description":"Invitation id"},"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string"},"resourceName":{"type":"string"},"inviteeEmail":{"type":"string"},"invitedByEmail":{"type":"string","description":"Who sent it"},"role":{"type":"string","enum":["viewer","editor"],"description":"Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"},"status":{"type":"string","enum":["pending","accepted","rejected"]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"acceptedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["id","resourceType","resourceId","resourceName","inviteeEmail","invitedByEmail","role","status","createdAt","acceptedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/sharing/invitations/received":{"get":{"operationId":"sharing.invitations.received","summary":"Pending invitations addressed to the caller","description":"Requires `account:read` (View invitations and notifications addressed to the user).","tags":["Sharing"],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"invitations":{"type":"array","items":{"$ref":"#/components/schemas/Invitation"}}},"required":["invitations"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Cancel an invitation the caller manages. Cancelling an accepted invitation revokes the access it granted. Needs the admin scope of the invitation's resource type.

> Any API key can call this.\
> \
> Errors specific to this operation:\
> \- 403 \`forbidden\`: The grant is the caller's own

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Sharing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/sharing/invitations/{invitationId}":{"delete":{"operationId":"sharing.invitations.cancel","summary":"Cancel an invitation the caller manages. Cancelling an accepted invitation revokes the access it granted. Needs the admin scope of the invitation's resource type.","description":"Any API key can call this.\n\nErrors specific to this operation:\n- 403 `forbidden`: The grant is the caller's own","tags":["Sharing"],"parameters":[{"name":"invitationId","in":"path","required":true,"description":"Invitation id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"warning":{"description":"Set when a grid could not release the grantee's machines or spot groups; the access change stands","type":["string","null"]}},"required":["warning"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Accept an existing resource invitation addressed to the caller. For new team access, use organization invitations and add members to the resource

> Requires \`account:write\` (Accept or decline invitations, leave shared resources, manage notifications).\
> \
> Errors specific to this operation:\
> \- 409 \`no\_org\`: A grid share, and the caller owns no organization to take it

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Sharing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/sharing/invitations/{invitationId}/accept":{"post":{"operationId":"sharing.invitations.accept","summary":"Accept an existing resource invitation addressed to the caller. For new team access, use organization invitations and add members to the resource","description":"Requires `account:write` (Accept or decline invitations, leave shared resources, manage notifications).\n\nErrors specific to this operation:\n- 409 `no_org`: A grid share, and the caller owns no organization to take it","tags":["Sharing"],"parameters":[{"name":"invitationId","in":"path","required":true,"description":"Invitation id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string"},"resourceName":{"type":"string"}},"required":["resourceType","resourceId","resourceName"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Decline an invitation addressed to the caller

> Requires \`account:write\` (Accept or decline invitations, leave shared resources, manage notifications).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Sharing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/sharing/invitations/{invitationId}/decline":{"post":{"operationId":"sharing.invitations.decline","summary":"Decline an invitation addressed to the caller","description":"Requires `account:write` (Accept or decline invitations, leave shared resources, manage notifications).","tags":["Sharing"],"parameters":[{"name":"invitationId","in":"path","required":true,"description":"Invitation id","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"declined":{"type":"boolean","const":true}},"required":["declined"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Grids

Organize dedicated compute into grids for your applications.

Grids belong to organizations. Organization-level calls take ?org=; grid-ID calls resolve it from the grid. Admins create and manage grids; Developers deploy to accessible grids and Viewers inspect them. Per-person quota policies are configured in the portal, not through the public API.

## List the grids the caller can see: the organization's, then those shared with it

> Requires \`pools:read\` (View grids and servers).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Grids"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Grid":{"type":"object","properties":{"id":{"type":"string","description":"Grid id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"access":{"$ref":"#/components/schemas/Access"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"nodes":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers in the grid"},"autoCreated":{"type":"boolean","description":"Created by the platform rather than the user"},"priorityMode":{"type":"string","enum":["absolute","weighted"],"description":"How app priorities apply when the grid is contended"},"needsReconciliation":{"type":"boolean","description":"Membership changes are still being applied"},"volumes":{"type":"array","items":{"type":"object","properties":{"mountPath":{"type":"string"},"maxSize":{"type":"number"},"tag":{"type":"string"}},"required":["mountPath","maxSize","tag"],"additionalProperties":false}},"networks":{"type":"array","items":{"type":"object","properties":{"cidr":{"type":"string"},"tag":{"type":"string"}},"required":["cidr","tag"],"additionalProperties":false}}},"required":["id","name","org","access","visibility","createdBy","nodes","autoCreated","priorityMode","needsReconciliation","volumes","networks"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/grids":{"get":{"operationId":"grids.list","summary":"List the grids the caller can see: the organization's, then those shared with it","description":"Requires `pools:read` (View grids and servers).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Grids"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"grids":{"type":"array","items":{"$ref":"#/components/schemas/Grid"}},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]}},"required":["grids","nextCursor"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Create a grid in the organization (admins), optionally with servers attached

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Grids"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"IdempotentReplayed":{"description":"true when the response was replayed for a reused Idempotency-Key rather than produced anew","schema":{"type":"boolean"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"GridCreateResult":{"type":"object","properties":{"id":{"type":"string","description":"Grid id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"access":{"$ref":"#/components/schemas/Access"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"nodes":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers in the grid"},"autoCreated":{"type":"boolean","description":"Created by the platform rather than the user"},"priorityMode":{"type":"string","enum":["absolute","weighted"],"description":"How app priorities apply when the grid is contended"},"needsReconciliation":{"type":"boolean","description":"Membership changes are still being applied"},"volumes":{"type":"array","items":{"type":"object","properties":{"mountPath":{"type":"string"},"maxSize":{"type":"number"},"tag":{"type":"string"}},"required":["mountPath","maxSize","tag"],"additionalProperties":false}},"networks":{"type":"array","items":{"type":"object","properties":{"cidr":{"type":"string"},"tag":{"type":"string"}},"required":["cidr","tag"],"additionalProperties":false}},"warning":{"description":"Set when the grid was created but a follow-up step failed","type":["string","null"]}},"required":["id","name","org","access","visibility","createdBy","nodes","autoCreated","priorityMode","needsReconciliation","volumes","networks","warning"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/grids":{"post":{"operationId":"grids.create","summary":"Create a grid in the organization (admins), optionally with servers attached","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Grids"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string of at most 255 characters. A retry with the same key and body returns the first call's response (with Idempotent-Replayed: true) instead of acting twice; a different body is refused with 409 idempotency_conflict, and a retry while the first call is still running gets 409 request_in_progress. Keys are kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^\\S+$"}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Grid name"},"visibility":{"description":"Who in the organization can see and deploy to it; private by default","type":"string","enum":["private","org"]},"volumes":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"mountPath":{"type":"string","minLength":1,"description":"Host path mounted into apps on the grid"},"maxSize":{"type":"integer","minimum":0,"description":"Size cap in bytes; 0 for unlimited"},"tag":{"description":"Label apps use to pick the volume","type":"string"}},"required":["mountPath","maxSize"],"additionalProperties":false},"description":"Shared volumes available to apps on the grid; replaces the stored list"},"networks":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"cidr":{"type":"string","minLength":1,"description":"Network range in CIDR notation"},"tag":{"description":"Label apps use to pick the network","type":"string"}},"required":["cidr"],"additionalProperties":false},"description":"Networks available to apps on the grid; replaces the stored list"},"nodes":{"description":"Addresses of servers to attach","maxItems":100,"type":"array","items":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"}}},"required":["name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Created","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GridCreateResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Get a grid and its membership

> Requires \`pools:read\` (View grids and servers).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Grids"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Grid":{"type":"object","properties":{"id":{"type":"string","description":"Grid id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"access":{"$ref":"#/components/schemas/Access"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"nodes":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers in the grid"},"autoCreated":{"type":"boolean","description":"Created by the platform rather than the user"},"priorityMode":{"type":"string","enum":["absolute","weighted"],"description":"How app priorities apply when the grid is contended"},"needsReconciliation":{"type":"boolean","description":"Membership changes are still being applied"},"volumes":{"type":"array","items":{"type":"object","properties":{"mountPath":{"type":"string"},"maxSize":{"type":"number"},"tag":{"type":"string"}},"required":["mountPath","maxSize","tag"],"additionalProperties":false}},"networks":{"type":"array","items":{"type":"object","properties":{"cidr":{"type":"string"},"tag":{"type":"string"}},"required":["cidr","tag"],"additionalProperties":false}}},"required":["id","name","org","access","visibility","createdBy","nodes","autoCreated","priorityMode","needsReconciliation","volumes","networks"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/grids/{gridId}":{"get":{"operationId":"grids.get","summary":"Get a grid and its membership","description":"Requires `pools:read` (View grids and servers).","tags":["Grids"],"parameters":[{"name":"gridId","in":"path","required":true,"description":"Grid id","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Grid"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Change a grid's settings, visibility, membership or priority mode (admins). Membership changes are explicit lists of servers to add and to remove.

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Grids"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Grid":{"type":"object","properties":{"id":{"type":"string","description":"Grid id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"access":{"$ref":"#/components/schemas/Access"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"nodes":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers in the grid"},"autoCreated":{"type":"boolean","description":"Created by the platform rather than the user"},"priorityMode":{"type":"string","enum":["absolute","weighted"],"description":"How app priorities apply when the grid is contended"},"needsReconciliation":{"type":"boolean","description":"Membership changes are still being applied"},"volumes":{"type":"array","items":{"type":"object","properties":{"mountPath":{"type":"string"},"maxSize":{"type":"number"},"tag":{"type":"string"}},"required":["mountPath","maxSize","tag"],"additionalProperties":false}},"networks":{"type":"array","items":{"type":"object","properties":{"cidr":{"type":"string"},"tag":{"type":"string"}},"required":["cidr","tag"],"additionalProperties":false}}},"required":["id","name","org","access","visibility","createdBy","nodes","autoCreated","priorityMode","needsReconciliation","volumes","networks"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/grids/{gridId}":{"patch":{"operationId":"grids.update","summary":"Change a grid's settings, visibility, membership or priority mode (admins). Membership changes are explicit lists of servers to add and to remove.","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).","tags":["Grids"],"parameters":[{"name":"gridId","in":"path","required":true,"description":"Grid id","schema":{"type":"string","minLength":1}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Grid name"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"volumes":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"mountPath":{"type":"string","minLength":1,"description":"Host path mounted into apps on the grid"},"maxSize":{"type":"integer","minimum":0,"description":"Size cap in bytes; 0 for unlimited"},"tag":{"description":"Label apps use to pick the volume","type":"string"}},"required":["mountPath","maxSize"],"additionalProperties":false},"description":"Shared volumes available to apps on the grid; replaces the stored list"},"networks":{"maxItems":16,"type":"array","items":{"type":"object","properties":{"cidr":{"type":"string","minLength":1,"description":"Network range in CIDR notation"},"tag":{"description":"Label apps use to pick the network","type":"string"}},"required":["cidr"],"additionalProperties":false},"description":"Networks available to apps on the grid; replaces the stored list"},"addNodes":{"description":"Servers to add to the grid","maxItems":500,"type":"array","items":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"}},"removeNodes":{"description":"Servers to remove from the grid","maxItems":500,"type":"array","items":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"}},"priorityMode":{"description":"How app priorities apply when the grid is contended","type":"string","enum":["absolute","weighted"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Grid"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Delete a grid (admins). Its servers are kept; sharing grants and invitations are retired.

> Requires \`pools:admin\` (Delete grids and manage who they are shared with).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Grids"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/grids/{gridId}":{"delete":{"operationId":"grids.delete","summary":"Delete a grid (admins). Its servers are kept; sharing grants and invitations are retired.","description":"Requires `pools:admin` (Delete grids and manage who they are shared with).","tags":["Grids"],"parameters":[{"name":"gridId","in":"path","required":true,"description":"Grid id","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Id of the deleted grid"},"deleted":{"type":"boolean","const":true}},"required":["id","deleted"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Set a grid member's scheduling priority, -100 to 100. Higher wins when the grid is contended; -100 means backup only. Admins of the grid's organization.

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Grids"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/grids/{gridId}/servers/{address}":{"patch":{"operationId":"grids.servers.setPriority","summary":"Set a grid member's scheduling priority, -100 to 100. Higher wins when the grid is contended; -100 means backup only. Admins of the grid's organization.","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).","tags":["Grids"],"parameters":[{"name":"gridId","in":"path","required":true,"description":"Grid id","schema":{"type":"string","minLength":1}},{"name":"address","in":"path","required":true,"description":"Server address; must be a member of the grid","schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"priority":{"type":"integer","minimum":-100,"maximum":100,"description":"-100 (backup only) to 100"}},"required":["priority"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"gridId":{"type":"string"},"address":{"type":"string"},"priority":{"type":"integer"}},"required":["gridId","address","priority"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Start an app that targets the grid, as an admin of the grid's organization, without a role on the app itself. The app's creator is notified and its organization is billed for it.

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys), \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\
> \
> Errors specific to this operation:\
> \- 403 \`forbidden\`: The backend refused the action for this grid role\
> \- 409 \`app\_not\_on\_grid\`: The app does not target (or run on) this grid

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Grids"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/grids/{gridId}/apps/{appGuid}/start":{"post":{"operationId":"grids.apps.start","summary":"Start an app that targets the grid, as an admin of the grid's organization, without a role on the app itself. The app's creator is notified and its organization is billed for it.","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys), `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\n\nErrors specific to this operation:\n- 403 `forbidden`: The backend refused the action for this grid role\n- 409 `app_not_on_grid`: The app does not target (or run on) this grid","tags":["Grids"],"parameters":[{"name":"gridId","in":"path","required":true,"description":"Grid id","schema":{"type":"string","minLength":1}},{"name":"appGuid","in":"path","required":true,"description":"Runtime identifier of the app (guid on list_apps, get_server)","schema":{"type":"string","minLength":1}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"description":"Shown to the app's creator in their notification","type":"string","maxLength":300}}}}}},"responses":{"202":{"description":"Accepted","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"gridId":{"type":"string"},"appGuid":{"type":"string"},"appId":{"description":"The app's id when it is known to the portal","type":["string","null"]},"appName":{"type":["string","null"]}},"required":["gridId","appGuid","appId","appName"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Stop an app running on the grid, as an admin of the grid's organization, without a role on the app itself. The app's creator is notified.

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 403 \`forbidden\`: The backend refused the action for this grid role\
> \- 409 \`app\_not\_on\_grid\`: The app does not target (or run on) this grid

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Grids"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/grids/{gridId}/apps/{appGuid}/stop":{"post":{"operationId":"grids.apps.stop","summary":"Stop an app running on the grid, as an admin of the grid's organization, without a role on the app itself. The app's creator is notified.","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 403 `forbidden`: The backend refused the action for this grid role\n- 409 `app_not_on_grid`: The app does not target (or run on) this grid","tags":["Grids"],"parameters":[{"name":"gridId","in":"path","required":true,"description":"Grid id","schema":{"type":"string","minLength":1}},{"name":"appGuid","in":"path","required":true,"description":"Runtime identifier of the app (guid on list_apps, get_server)","schema":{"type":"string","minLength":1}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"description":"Shown to the app's creator in their notification","type":"string","maxLength":300}}}}}},"responses":{"202":{"description":"Accepted","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"gridId":{"type":"string"},"appGuid":{"type":"string"},"appId":{"description":"The app's id when it is known to the portal","type":["string","null"]},"appName":{"type":["string","null"]}},"required":["gridId","appGuid","appId","appName"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Servers

Provision servers, follow orders and manage machine lifecycles.

addedBy names the person who added a server, or is null without members:read, for another organization’s server or when nobody is recorded. Machine logs are raw output and may include sensitive data.

Pass the organization slug as ?org= where listed in the operation parameters. Admins manage servers, orders and SSH keys; other members can inspect accessible infrastructure. Use name as the display label and address as the stable identifier.

## List the organization's servers, those it rents, and pending orders. A shared grid's servers count as usable

> Requires \`pools:read\` (View grids and servers).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Server":{"type":"object","properties":{"address":{"type":"string","description":"Server (node) address"},"name":{"description":"Display name: the caller's own name for the server when set; else, for a server rented from the marketplace, its hardware and Kinesis region; else the hostname","type":["string","null"]},"hostname":{"description":"Null for servers rented from the marketplace","type":["string","null"]},"csp":{"description":"Cloud provider; null for servers rented from the marketplace","type":["string","null"]},"region":{"description":"Provider region; null for servers rented from the marketplace","type":["string","null"]},"zone":{"description":"Provider zone; null for servers rented from the marketplace","type":["string","null"]},"instanceType":{"description":"Provider instance type; null for servers rented from the marketplace","type":["string","null"]},"kcRegion":{"description":"Kinesis region the server serves, e.g. us-west","type":["string","null"]},"owned":{"type":"boolean","description":"The organization owns this server (false for servers seen through a shared grid)"},"ownerName":{"description":"The organization that owns it; null for rented servers","type":["string","null"]},"addedBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who added it to the organization. Null when nobody is on record, for another organization's server, and for a credential without members:read"},"state":{"type":"string"},"hibernation":{"type":"string","description":"active, hibernated, hibernating or waking"},"lastPulseAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"specs":{"type":"object","properties":{"cpuThreads":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"gpus":{"type":"array","items":{"type":"object","properties":{"model":{"type":"string"},"count":{"type":"number"},"vramBytes":{"type":"number"}},"required":["model","count","vramBytes"],"additionalProperties":false}}},"required":["cpuThreads","ramBytes","storageBytes","gpus"],"additionalProperties":false},"utilization":{"type":"object","properties":{"cpu":{"type":"number"},"ram":{"type":"number"},"storage":{"type":"number"},"gpu":{"type":"number"},"vram":{"type":"number"}},"required":["cpu","ram","storage","gpu","vram"],"additionalProperties":false,"description":"Percentages from the last pulse"},"runningApps":{"type":"integer"},"lease":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"gridId":{"type":"string"},"availableUntil":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"priceCentsPerHour":{"type":"number"}},"required":["id","gridId","availableUntil","priceCentsPerHour"],"additionalProperties":false},{"type":"null"}],"description":"Spot lease, for servers rented from the marketplace"},"provisionStatus":{"description":"Provisioning order status while the server is being created","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["address","name","hostname","csp","region","zone","instanceType","kcRegion","owned","ownerName","addedBy","state","hibernation","lastPulseAt","specs","utilization","runningApps","lease","provisionStatus","createdAt"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"PendingServer":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"address":{"description":"Server address once the machine has registered; null while it is still being created","type":["string","null"]},"status":{"type":"string"},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"csp":{"type":["string","null"]},"region":{"type":["string","null"]},"instanceType":{"type":["string","null"]},"gridIds":{"type":"array","items":{"type":"string"},"description":"Grids the server joins once it registers"},"attempts":{"type":"object","properties":{"made":{"type":"integer"},"max":{"type":"integer"}},"required":["made","max"],"additionalProperties":false},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["orderId","address","status","failure","csp","region","instanceType","gridIds","attempts","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers":{"get":{"operationId":"servers.list","summary":"List the organization's servers, those it rents, and pending orders. A shared grid's servers count as usable","description":"Requires `pools:read` (View grids and servers).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"servers":{"type":"array","items":{"$ref":"#/components/schemas/Server"},"description":"One page of servers, by address"},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]},"pending":{"type":"array","items":{"$ref":"#/components/schemas/PendingServer"},"description":"Provisioning orders that have not produced a server yet; complete on every page"}},"required":["servers","nextCursor","pending"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Order dedicated servers from a cloud provider. Checks the quota and the balance first; set dryRun to get the cost estimate without ordering. Poll servers.orders.get with the returned order id to follow the order.

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys), \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\
> \
> Errors specific to this operation:\
> \- 402 \`insufficient\_balance\`: The balance does not cover the minimum runtime of the order; topUpNeededCents says how much to add (billingUrl)\
> \- 403 \`provision\_quota\`: The organization's provisioning limit is reached\
> \- 400 \`no\_provider\_for\_region\`: No provider serves the requested Kinesis region\
> \- 400 \`ephemeral\_ssd\_unavailable\`: The instance type has no bundled local SSD; set storageSizeGb instead\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"IdempotentReplayed":{"description":"true when the response was replayed for a reused Idempotency-Key rather than produced anew","schema":{"type":"boolean"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ProvisionResult":{"type":"object","properties":{"dryRun":{"type":"boolean"},"csp":{"type":"string"},"region":{"type":"string"},"instanceType":{"type":"string"},"count":{"type":"integer"},"estimate":{"$ref":"#/components/schemas/RunwayEstimate"},"orders":{"type":"array","items":{"$ref":"#/components/schemas/ProvisionOrder"},"description":"Empty for a dry run"}},"required":["dryRun","csp","region","instanceType","count","estimate","orders"],"additionalProperties":false},"RunwayEstimate":{"type":"object","properties":{"instanceCount":{"type":"integer"},"totalHourlyCostCents":{"type":"number","description":"Hourly cost of the whole order"},"totalRequiredCents":{"type":"number","description":"Balance needed to cover the minimum runtime"},"minimumHours":{"type":"number","description":"Hours of runtime the balance must cover before an order is accepted"},"balanceCents":{"type":"number"},"overLimitCents":{"type":"number"},"availableRunwayCents":{"type":"number","description":"Balance plus the over-limit allowance"},"topUpNeededCents":{"type":"number","description":"0 when the balance suffices"}},"required":["instanceCount","totalHourlyCostCents","totalRequiredCents","minimumHours","balanceCents","overLimitCents","availableRunwayCents","topUpNeededCents"],"additionalProperties":false},"ProvisionOrder":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"status":{"type":"string"},"count":{"type":"integer","description":"Servers in the order"},"csp":{"type":["string","null"]},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"dismissed":{"type":"boolean","description":"Hidden from the pending list (servers.orders.dismiss)"}},"required":["orderId","status","count","csp","failure","createdAt","updatedAt","dismissed"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PaymentRequired":{"description":"The balance or the card does not cover the action: insufficient_balance, balance_over_limit, card_declined, authentication_required or blocked_by_risk","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/provision":{"post":{"operationId":"servers.provision","summary":"Order dedicated servers from a cloud provider. Checks the quota and the balance first; set dryRun to get the cost estimate without ordering. Poll servers.orders.get with the returned order id to follow the order.","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys), `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\n\nErrors specific to this operation:\n- 402 `insufficient_balance`: The balance does not cover the minimum runtime of the order; topUpNeededCents says how much to add (billingUrl)\n- 403 `provision_quota`: The organization's provisioning limit is reached\n- 400 `no_provider_for_region`: No provider serves the requested Kinesis region\n- 400 `ephemeral_ssd_unavailable`: The instance type has no bundled local SSD; set storageSizeGb instead\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string of at most 255 characters. A retry with the same key and body returns the first call's response (with Idempotent-Replayed: true) instead of acting twice; a different body is refused with 409 idempotency_conflict, and a retry while the first call is still running gets 409 request_in_progress. Keys are kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^\\S+$"}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"csp":{"type":"string","enum":["kinesis","aws","azure"],"description":"Cloud provider"},"region":{"type":"string","minLength":1,"description":"Provider region id (see catalog.regions)"},"zone":{"description":"Availability zone; omit to let the provider choose","type":"string","minLength":1},"instanceType":{"type":"string","minLength":1,"description":"Instance type id (see catalog.instanceTypes)"},"count":{"description":"Servers to order, 1 to 10 (default 1)","type":"integer","minimum":1,"maximum":10},"storageSizeGb":{"description":"Disk per server in GB, 50 to 10000; required unless useEphemeralSsd is set","type":"integer","minimum":50,"maximum":10000},"useEphemeralSsd":{"description":"Use the instance's bundled local SSD instead of a network volume (Kinesis compute's large GPU instance types that can't hibernate only)","type":"boolean"},"gridIds":{"description":"Grids the servers join once they register","maxItems":10,"type":"array","items":{"type":"string","minLength":1}},"dryRun":{"description":"Run every check and return the estimate without ordering","type":"boolean"}},"required":["csp","region","instanceType"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisionResult"}}}},"202":{"description":"Accepted","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisionResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Destroy dedicated servers. They leave every grid first. Irreversible.

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ServerActionResult":{"type":"object","properties":{"address":{"type":"string"},"ok":{"type":"boolean"},"error":{"type":["string","null"]}},"required":["address","ok","error"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/deprovision":{"post":{"operationId":"servers.deprovision","summary":"Destroy dedicated servers. They leave every grid first. Irreversible.","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"addresses":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"description":"Server addresses, at most 50"}},"required":["addresses"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/ServerActionResult"},"description":"One entry per requested server"}},"required":["results"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Hibernate servers: the machine stops and stops billing compute, its disk is kept

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"PowerActionResult":{"type":"object","properties":{"address":{"type":"string"},"ok":{"type":"boolean"},"error":{"type":["string","null"]},"reason":{"anyOf":[{"type":"string","enum":["out_of_stock","not_hibernatable","not_active","unknown"]},{"type":"null"}],"description":"Why the request was refused or failed: out_of_stock means retry later; not_hibernatable and not_active are screened before the backend is asked"}},"required":["address","ok","error","reason"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/hibernate":{"post":{"operationId":"servers.hibernate","summary":"Hibernate servers: the machine stops and stops billing compute, its disk is kept","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"addresses":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"description":"Server addresses, at most 50"}},"required":["addresses"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/PowerActionResult"},"description":"One entry per requested server; ok means the request was accepted, the state change is asynchronous"}},"required":["results"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Wake hibernated servers. Fails with out\_of\_stock when the provider has no capacity.

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys), \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"PowerActionResult":{"type":"object","properties":{"address":{"type":"string"},"ok":{"type":"boolean"},"error":{"type":["string","null"]},"reason":{"anyOf":[{"type":"string","enum":["out_of_stock","not_hibernatable","not_active","unknown"]},{"type":"null"}],"description":"Why the request was refused or failed: out_of_stock means retry later; not_hibernatable and not_active are screened before the backend is asked"}},"required":["address","ok","error","reason"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/wake":{"post":{"operationId":"servers.wake","summary":"Wake hibernated servers. Fails with out_of_stock when the provider has no capacity.","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys), `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"addresses":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"description":"Server addresses, at most 50"}},"required":["addresses"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/PowerActionResult"},"description":"One entry per requested server; ok means the request was accepted, the state change is asynchronous"}},"required":["results"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Reboot dedicated servers

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ServerActionResult":{"type":"object","properties":{"address":{"type":"string"},"ok":{"type":"boolean"},"error":{"type":["string","null"]}},"required":["address","ok","error"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/reboot":{"post":{"operationId":"servers.reboot","summary":"Reboot dedicated servers","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"addresses":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"description":"Server addresses, at most 50"}},"required":["addresses"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/ServerActionResult"},"description":"One entry per requested server"}},"required":["results"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Get a provisioning order the caller placed and the machines it has produced. Completed, replacement and dismissed orders are readable too. Poll this after servers.provision or servers.replace until done.

> Requires \`pools:read\` (View grids and servers).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ProvisionOrderDetail":{"type":"object","properties":{"order":{"$ref":"#/components/schemas/ProvisionOrder"},"instances":{"type":"array","items":{"$ref":"#/components/schemas/PendingServer"},"description":"One entry per machine in the order; address is set once the machine has registered"},"done":{"type":"boolean","description":"The order reached a terminal state: complete, failed or cancelled"}},"required":["order","instances","done"],"additionalProperties":false},"ProvisionOrder":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"status":{"type":"string"},"count":{"type":"integer","description":"Servers in the order"},"csp":{"type":["string","null"]},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"dismissed":{"type":"boolean","description":"Hidden from the pending list (servers.orders.dismiss)"}},"required":["orderId","status","count","csp","failure","createdAt","updatedAt","dismissed"],"additionalProperties":false},"PendingServer":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"address":{"description":"Server address once the machine has registered; null while it is still being created","type":["string","null"]},"status":{"type":"string"},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"csp":{"type":["string","null"]},"region":{"type":["string","null"]},"instanceType":{"type":["string","null"]},"gridIds":{"type":"array","items":{"type":"string"},"description":"Grids the server joins once it registers"},"attempts":{"type":"object","properties":{"made":{"type":"integer"},"max":{"type":"integer"}},"required":["made","max"],"additionalProperties":false},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["orderId","address","status","failure","csp","region","instanceType","gridIds","attempts","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/orders/{orderId}":{"get":{"operationId":"servers.orders.get","summary":"Get a provisioning order the caller placed and the machines it has produced. Completed, replacement and dismissed orders are readable too. Poll this after servers.provision or servers.replace until done.","description":"Requires `pools:read` (View grids and servers).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"orderId","in":"path","required":true,"description":"Provisioning order id","schema":{"type":"string","minLength":1}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisionOrderDetail"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Retry a failed provisioning order

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys), \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\
> \
> Errors specific to this operation:\
> \- 409 \`provision\_order\_failed\`: The backend refused the action for this order; the message says why\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ProvisionOrder":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"status":{"type":"string"},"count":{"type":"integer","description":"Servers in the order"},"csp":{"type":["string","null"]},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"dismissed":{"type":"boolean","description":"Hidden from the pending list (servers.orders.dismiss)"}},"required":["orderId","status","count","csp","failure","createdAt","updatedAt","dismissed"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/orders/{orderId}/retry":{"post":{"operationId":"servers.orders.retry","summary":"Retry a failed provisioning order","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys), `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\n\nErrors specific to this operation:\n- 409 `provision_order_failed`: The backend refused the action for this order; the message says why\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"orderId","in":"path","required":true,"description":"Provisioning order id","schema":{"type":"string","minLength":1}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"order":{"anyOf":[{"$ref":"#/components/schemas/ProvisionOrder"},{"type":"null"}],"description":"The resulting order, when the backend returned one"}},"required":["order"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Cancel an in-flight provisioning order, or hide a finished one from the pending list

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 409 \`provision\_order\_failed\`: The backend refused the action for this order; the message says why\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/servers/orders/{orderId}/dismiss":{"post":{"operationId":"servers.orders.dismiss","summary":"Cancel an in-flight provisioning order, or hide a finished one from the pending list","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 409 `provision_order_failed`: The backend refused the action for this order; the message says why\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"orderId","in":"path","required":true,"description":"Provisioning order id","schema":{"type":"string","minLength":1}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"orderId":{"type":"string"},"dismissed":{"type":"boolean","const":true}},"required":["orderId","dismissed"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Get a provision token and install command for attaching a machine. The one-line command attaches it (bring your own device) as one of the organization's servers. The same usable token comes back on repeated calls until it is consumed or expires; forceNew mints a fresh one.

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ByodToken":{"type":"object","properties":{"token":{"type":"string","description":"Provision token; single use"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"needVpn":{"type":"boolean","description":"The machine joins through Kinesis Remote Reach (VPN-backed)"},"expiresInSec":{"type":"integer"},"installCommand":{"type":"string","description":"Run this on the machine as root; it installs the agent and registers it"}},"required":["token","expiresAt","needVpn","expiresInSec","installCommand"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/byod-token":{"post":{"operationId":"servers.byodToken","summary":"Get a provision token and install command for attaching a machine. The one-line command attaches it (bring your own device) as one of the organization's servers. The same usable token comes back on repeated calls until it is consumed or expires; forceNew mints a fresh one.","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"expiresInSec":{"description":"Lifetime of a newly minted token, 3600 by default","type":"integer","minimum":60,"maximum":604800},"forceNew":{"description":"Mint a new token even when a usable one exists","type":"boolean"}}}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ByodToken"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## The Smart Lifecycle setting of each server the organization owns. On, the platform hibernates it when nothing runs on it (storage billing only) and wakes it when an app needs it

> Requires \`pools:read\` (View grids and servers).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/servers/smart-lifecycle":{"get":{"operationId":"servers.smartLifecycle.list","summary":"The Smart Lifecycle setting of each server the organization owns. On, the platform hibernates it when nothing runs on it (storage billing only) and wakes it when an app needs it","description":"Requires `pools:read` (View grids and servers).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"servers":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"enabled":{"type":"boolean","description":"The platform hibernates the server when idle and wakes it when an app needs it"}},"required":["address","enabled"],"additionalProperties":false},"description":"One entry per server the organization owns"}},"required":["servers"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Turn automatic hibernate and wake-up (Smart Lifecycle) on or off. For the org's servers

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/servers/smart-lifecycle":{"patch":{"operationId":"servers.smartLifecycle.update","summary":"Turn automatic hibernate and wake-up (Smart Lifecycle) on or off. For the org's servers","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"addresses":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"description":"Server addresses, at most 50"},"enabled":{"type":"boolean","description":"On or off"}},"required":["addresses","enabled"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"servers":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"enabled":{"type":"boolean","description":"The platform hibernates the server when idle and wakes it when an app needs it"}},"required":["address","enabled"],"additionalProperties":false},"description":"One entry per server the organization owns"}},"required":["servers"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Get one server: specs, utilization, apps, grids and provisioning order. Its own organization also sees its public address

> Requires \`pools:read\` (View grids and servers).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ServerDetail":{"type":"object","properties":{"address":{"type":"string","description":"Server (node) address"},"name":{"description":"Display name: the caller's own name for the server when set; else, for a server rented from the marketplace, its hardware and Kinesis region; else the hostname","type":["string","null"]},"hostname":{"description":"Null for servers rented from the marketplace","type":["string","null"]},"csp":{"description":"Cloud provider; null for servers rented from the marketplace","type":["string","null"]},"region":{"description":"Provider region; null for servers rented from the marketplace","type":["string","null"]},"zone":{"description":"Provider zone; null for servers rented from the marketplace","type":["string","null"]},"instanceType":{"description":"Provider instance type; null for servers rented from the marketplace","type":["string","null"]},"kcRegion":{"description":"Kinesis region the server serves, e.g. us-west","type":["string","null"]},"owned":{"type":"boolean","description":"The organization owns this server (false for servers seen through a shared grid)"},"ownerName":{"description":"The organization that owns it; null for rented servers","type":["string","null"]},"addedBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who added it to the organization. Null when nobody is on record, for another organization's server, and for a credential without members:read"},"state":{"type":"string"},"hibernation":{"type":"string","description":"active, hibernated, hibernating or waking"},"lastPulseAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"specs":{"type":"object","properties":{"cpuThreads":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"gpus":{"type":"array","items":{"type":"object","properties":{"model":{"type":"string"},"count":{"type":"number"},"vramBytes":{"type":"number"}},"required":["model","count","vramBytes"],"additionalProperties":false}}},"required":["cpuThreads","ramBytes","storageBytes","gpus"],"additionalProperties":false},"utilization":{"type":"object","properties":{"cpu":{"type":"number"},"ram":{"type":"number"},"storage":{"type":"number"},"gpu":{"type":"number"},"vram":{"type":"number"}},"required":["cpu","ram","storage","gpu","vram"],"additionalProperties":false,"description":"Percentages from the last pulse"},"runningApps":{"type":"integer"},"lease":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"gridId":{"type":"string"},"availableUntil":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"priceCentsPerHour":{"type":"number"}},"required":["id","gridId","availableUntil","priceCentsPerHour"],"additionalProperties":false},{"type":"null"}],"description":"Spot lease, for servers rented from the marketplace"},"provisionStatus":{"description":"Provisioning order status while the server is being created","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"ipAddress":{"description":"Public address; null unless the server belongs to the selected organization","type":["string","null"]},"version":{"description":"Agent version the server runs","type":["string","null"]},"countryCode":{"type":["string","null"]},"autoWakeup":{"type":"boolean","description":"The platform wakes the server when an app needs it"},"gridIds":{"type":"array","items":{"type":"string"},"description":"Grids the server belongs to, among those the caller can see"},"apps":{"type":"array","items":{"type":"object","properties":{"guid":{"type":"string","description":"Runtime identifier of the app (list_apps shows it as guid)"},"image":{"type":["string","null"]},"state":{"type":"string","description":"Container state on this server"},"targetState":{"type":"string","enum":["running","stopped","unknown"]},"reportedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["guid","image","state","targetState","reportedAt"],"additionalProperties":false},"description":"Apps placed on the server and their container state there"},"order":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Provisioning order id"},"status":{"type":"string"},"failure":{"type":["string","null"]},"attempts":{"type":"object","properties":{"made":{"type":"integer"},"max":{"type":"integer"}},"required":["made","max"],"additionalProperties":false},"awaitingStockSince":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"Set while a wake waits for provider stock"}},"required":["id","status","failure","attempts","awaitingStockSince"],"additionalProperties":false},{"type":"null"}],"description":"The provisioning order behind a Kinesis-provisioned server; null for an attached machine"}},"required":["address","name","hostname","csp","region","zone","instanceType","kcRegion","owned","ownerName","addedBy","state","hibernation","lastPulseAt","specs","utilization","runningApps","lease","provisionStatus","createdAt","ipAddress","version","countryCode","autoWakeup","gridIds","apps","order"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/{address}":{"get":{"operationId":"servers.get","summary":"Get one server: specs, utilization, apps, grids and provisioning order. Its own organization also sees its public address","description":"Requires `pools:read` (View grids and servers).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"address","in":"path","required":true,"description":"Server address","schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerDetail"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Read the newest lines of a server's own logs. Agent, runtime health, installation and GPU setup, for one of the organization's servers. Narrow the window with since and until; lines come back oldest to newest. They are the machine's logs as written and may include sensitive data.

> Requires \`monitoring:read\` (Read usage metrics, app logs, build logs and server logs).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ServerLogs":{"type":"object","properties":{"logs":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"source":{"type":"string","description":"Component that wrote the line, e.g. the agent, the runtime, GPU setup"},"level":{"type":"string","enum":["trace","info","warning","error","unknown"]},"text":{"type":"string"}},"required":["at","source","level","text"],"additionalProperties":false},"description":"Oldest to newest; the last line is the most recent"},"truncated":{"type":"boolean","description":"The window holds more lines than limit; the oldest were dropped (pass until to read further back)"}},"required":["logs","truncated"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/{address}/logs":{"get":{"operationId":"servers.logs","summary":"Read the newest lines of a server's own logs. Agent, runtime health, installation and GPU setup, for one of the organization's servers. Narrow the window with since and until; lines come back oldest to newest. They are the machine's logs as written and may include sensitive data.","description":"Requires `monitoring:read` (Read usage metrics, app logs, build logs and server logs).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"address","in":"path","required":true,"description":"Server address","schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"since","in":"query","required":false,"description":"Earliest line to include; omit for no lower bound","schema":{"type":"string","format":"date-time"}},{"name":"until","in":"query","required":false,"description":"Latest line to include; omit for now","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","required":false,"description":"Newest lines to return within the window, 500 by default","schema":{"type":"integer","minimum":1,"maximum":5000}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerLogs"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## The organization's SSH public keys and where each is installed (admins). Also its servers a key can be installed on (those added without Kinesis Remote Reach)

> Requires \`pools:read\` (View grids and servers).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"SshKey":{"type":"object","properties":{"id":{"type":"string","description":"Key id"},"label":{"type":"string"},"fingerprint":{"type":"string"},"servers":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers the key is installed on"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["id","label","fingerprint","servers","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/ssh-keys":{"get":{"operationId":"sshKeys.list","summary":"The organization's SSH public keys and where each is installed (admins). Also its servers a key can be installed on (those added without Kinesis Remote Reach)","description":"Requires `pools:read` (View grids and servers).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/SshKey"}},"eligibleServers":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"csp":{"type":["string","null"]},"region":{"type":["string","null"]},"instanceType":{"type":["string","null"]},"ipAddress":{"description":"What ssh connects to","type":["string","null"]}},"required":["address","csp","region","instanceType","ipAddress"],"additionalProperties":false},"description":"Servers a key can be assigned to"}},"required":["keys","eligibleServers"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Register an SSH public key under a label. OpenSSH format; assign it to servers afterwards

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"SshKey":{"type":"object","properties":{"id":{"type":"string","description":"Key id"},"label":{"type":"string"},"fingerprint":{"type":"string"},"servers":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers the key is installed on"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["id","label","fingerprint","servers","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/ssh-keys":{"post":{"operationId":"sshKeys.create","summary":"Register an SSH public key under a label. OpenSSH format; assign it to servers afterwards","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":64,"description":"A name for the key"},"publicKey":{"type":"string","minLength":1,"maxLength":16384,"description":"The public key, e.g. the contents of id_ed25519.pub"}},"required":["label","publicKey"]}}}},"responses":{"201":{"description":"Created","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SshKey"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Delete an SSH key; it is removed from every server it was installed on

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/ssh-keys/{keyId}":{"delete":{"operationId":"sshKeys.delete","summary":"Delete an SSH key; it is removed from every server it was installed on","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"keyId","in":"path","required":true,"description":"Key id from the list","schema":{"type":"string","minLength":1}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","const":true}},"required":["deleted"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Install an SSH key on servers (eligibleServers in sshKeys.list); ssh \<user>@\<ipAddress> then works with the matching private key

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"SshKey":{"type":"object","properties":{"id":{"type":"string","description":"Key id"},"label":{"type":"string"},"fingerprint":{"type":"string"},"servers":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers the key is installed on"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["id","label","fingerprint","servers","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/ssh-keys/{keyId}/servers":{"post":{"operationId":"sshKeys.assign","summary":"Install an SSH key on servers (eligibleServers in sshKeys.list); ssh <user>@<ipAddress> then works with the matching private key","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"keyId","in":"path","required":true,"description":"Key id from the list","schema":{"type":"string","minLength":1}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"addresses":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"},"description":"Server addresses, at most 50"}},"required":["addresses"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SshKey"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Remove an SSH key from servers

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"SshKey":{"type":"object","properties":{"id":{"type":"string","description":"Key id"},"label":{"type":"string"},"fingerprint":{"type":"string"},"servers":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers the key is installed on"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["id","label","fingerprint","servers","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/ssh-keys/{keyId}/servers":{"delete":{"operationId":"sshKeys.unassign","summary":"Remove an SSH key from servers","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"keyId","in":"path","required":true,"description":"Key id from the list","schema":{"type":"string","minLength":1}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"addresses","in":"query","required":true,"description":"Server addresses, at most 50","schema":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"}},"style":"form","explode":true}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SshKey"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Provision an identical server and retire this one once the new one is up. The retired server's disk is not kept. Poll servers.orders.get with the returned order id to follow the replacement.

> Requires \`pools:write\` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys), \`compute:spend\` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Servers"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"IdempotentReplayed":{"description":"true when the response was replayed for a reused Idempotency-Key rather than produced anew","schema":{"type":"boolean"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ProvisionOrder":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"status":{"type":"string"},"count":{"type":"integer","description":"Servers in the order"},"csp":{"type":["string","null"]},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"dismissed":{"type":"boolean","description":"Hidden from the pending list (servers.orders.dismiss)"}},"required":["orderId","status","count","csp","failure","createdAt","updatedAt","dismissed"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/servers/{address}/replace":{"post":{"operationId":"servers.replace","summary":"Provision an identical server and retire this one once the new one is up. The retired server's disk is not kept. Poll servers.orders.get with the returned order id to follow the replacement.","description":"Requires `pools:write` (Edit grids, manage their servers, attach machines, set Smart Lifecycle and SSH keys), `compute:spend` (Spend credits: start, scale or resize apps, provision or wake servers, rent spot instances).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Servers"],"parameters":[{"name":"address","in":"path","required":true,"description":"Address of the server to replace","schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Any unique string of at most 255 characters. A retry with the same key and body returns the first call's response (with Idempotent-Replayed: true) instead of acting twice; a different body is refused with 409 idempotency_conflict, and a retry while the first call is still running gets 409 request_in_progress. Keys are kept for 24 hours.","schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^\\S+$"}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"202":{"description":"Accepted","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"type":"object","properties":{"order":{"anyOf":[{"$ref":"#/components/schemas/ProvisionOrder"},{"type":"null"}],"description":"The resulting order, when the backend returned one"}},"required":["order"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Catalog

Discover cloud regions, zones and available instance types.

## List the regions a provider can provision servers in

> Requires \`pools:read\` (View grids and servers).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Catalog"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Region":{"type":"object","properties":{"id":{"type":"string","description":"Region id to use when provisioning"},"name":{"type":"string"},"location":{"type":"string","description":"City or area"},"recommended":{"type":"boolean"}},"required":["id","name","location","recommended"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/catalog/{csp}/regions":{"get":{"operationId":"catalog.regions","summary":"List the regions a provider can provision servers in","description":"Requires `pools:read` (View grids and servers).","tags":["Catalog"],"parameters":[{"name":"csp","in":"path","required":true,"description":"Cloud provider","schema":{"type":"string","enum":["kinesis","aws","azure"]}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"regions":{"type":"array","items":{"$ref":"#/components/schemas/Region"}},"defaultRegion":{"type":"string","description":"Region id chosen by default"}},"required":["regions","defaultRegion"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## List the availability zones of a provider region

> Requires \`pools:read\` (View grids and servers).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Catalog"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/catalog/{csp}/regions/{region}/zones":{"get":{"operationId":"catalog.zones","summary":"List the availability zones of a provider region","description":"Requires `pools:read` (View grids and servers).","tags":["Catalog"],"parameters":[{"name":"csp","in":"path","required":true,"description":"Cloud provider","schema":{"type":"string","enum":["kinesis","aws","azure"]}},{"name":"region","in":"path","required":true,"description":"Provider region id","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"zones":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"],"additionalProperties":false}}},"required":["zones"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## List instance types available in a provider region: the popular picks by default, a search, or everything

> Requires \`pools:read\` (View grids and servers).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Catalog"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"InstanceType":{"type":"object","properties":{"id":{"type":"string","description":"Instance type id to use when provisioning"},"name":{"type":"string"},"category":{"type":"string","description":"General Purpose, Compute Optimized, GPU, ..."},"specs":{"type":"object","properties":{"vCpus":{"type":"number"},"memory":{"type":"string","description":"Human-readable, e.g. 8 GiB"},"storage":{"type":"string"},"networkPerformance":{"type":"string"},"gpuType":{"type":"string"},"gpuManufacturer":{"type":"string"},"gpuCount":{"type":"number"},"gpuMemory":{"type":"string"}},"required":["vCpus","memory"],"additionalProperties":false},"pricePerHour":{"type":"number","description":"Customer price in USD per hour"},"inStock":{"description":"null when the provider does not report stock","type":["boolean","null"]}},"required":["id","name","category","specs","pricePerHour","inStock"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/catalog/{csp}/regions/{region}/instance-types":{"get":{"operationId":"catalog.instanceTypes","summary":"List instance types available in a provider region: the popular picks by default, a search, or everything","description":"Requires `pools:read` (View grids and servers).","tags":["Catalog"],"parameters":[{"name":"csp","in":"path","required":true,"description":"Cloud provider","schema":{"type":"string","enum":["kinesis","aws","azure"]}},{"name":"region","in":"path","required":true,"description":"Provider region id","schema":{"type":"string","minLength":1}},{"name":"zone","in":"query","required":false,"description":"Limit to one availability zone","schema":{"type":"string","minLength":1}},{"name":"search","in":"query","required":false,"description":"Substring to search names and GPU types for","schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"all","in":"query","required":false,"description":"List the full catalog instead of the popular picks","schema":{"type":"boolean"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"instanceTypes":{"type":"array","items":{"$ref":"#/components/schemas/InstanceType"},"description":"Cheapest first"}},"required":["instanceTypes"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Marketplace

Browse spot compute by hardware profile and location.

## Browse spot capacity on the marketplace by hardware profile and country

> Requires \`pools:read\` (View grids and servers).

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Marketplace"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"MarketplaceBrowse":{"type":"object","properties":{"listings":{"type":"array","items":{"$ref":"#/components/schemas/MarketplaceListing"}},"summary":{"type":"object","properties":{"availableMachines":{"type":"integer"},"configurations":{"type":"integer"},"fromPriceCentsPerGpuHour":{"type":"number"},"fromPriceCentsPerHour":{"type":"number"}},"required":["availableMachines","configurations"],"additionalProperties":false},"truncated":{"type":"boolean","description":"The scan hit its cap; counts are lower bounds"}},"required":["listings","summary","truncated"],"additionalProperties":false},"MarketplaceListing":{"type":"object","properties":{"configId":{"type":"string","description":"Stable id of this hardware profile and country bucket"},"hardwareProfile":{"type":"string"},"gpuModel":{"description":"null for CPU-only machines","type":["string","null"]},"gpusPerMachine":{"type":"integer"},"vramGbPerGpu":{"type":"number"},"cpuThreads":{"type":"number"},"minCpuPackages":{"description":"Smallest physical CPU count in the bucket","type":"number"},"ramGb":{"type":"number"},"storageGb":{"type":"number"},"countryCode":{"type":"string","description":"ISO country code; empty when unreported"},"locationLabel":{"type":"string"},"tierIds":{"type":"array","items":{"type":"string"}},"isVetted":{"type":"boolean"},"totalMachines":{"type":"integer"},"availableMachines":{"type":"integer"},"minPriceCentsPerHour":{"type":"number","description":"Cheapest machine-hour in the bucket"},"maxPriceCentsPerHour":{"type":"number"},"pricePerGpuHourCents":{"type":"number"},"guaranteedDays":{"description":"Weakest availability guarantee in the bucket","type":"number"},"isOpenEnded":{"type":"boolean","description":"Every machine is available until its provider changes its mind"},"noticeHours":{"type":"number"}},"required":["configId","hardwareProfile","gpuModel","gpusPerMachine","countryCode","locationLabel","tierIds","isVetted","totalMachines","availableMachines","minPriceCentsPerHour","maxPriceCentsPerHour","isOpenEnded","noticeHours"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/marketplace":{"get":{"operationId":"marketplace.browse","summary":"Browse spot capacity on the marketplace by hardware profile and country","description":"Requires `pools:read` (View grids and servers).","tags":["Marketplace"],"parameters":[{"name":"gpuTypes","in":"query","required":false,"description":"GPU models to include, e.g. H100","schema":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"style":"form","explode":true},{"name":"noGpu","in":"query","required":false,"description":"CPU-only machines","schema":{"type":"boolean"}},{"name":"gpusPerMachine","in":"query","required":false,"description":"Exact GPU count per machine","schema":{"type":"integer","exclusiveMinimum":0}},{"name":"minCpus","in":"query","required":false,"description":"Minimum physical CPU packages","schema":{"type":"integer","exclusiveMinimum":0}},{"name":"minRamGb","in":"query","required":false,"description":"Minimum RAM in GB","schema":{"type":"integer","exclusiveMinimum":0}},{"name":"minStorageGb","in":"query","required":false,"description":"Minimum disk in GB","schema":{"type":"integer","exclusiveMinimum":0}},{"name":"location","in":"query","required":false,"description":"Marketplace location key","schema":{"type":"string","minLength":1}},{"name":"vettedOnly","in":"query","required":false,"description":"Only machines from vetted providers","schema":{"type":"boolean"}},{"name":"tierIds","in":"query","required":false,"description":"Provider tiers to include","schema":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"style":"form","explode":true},{"name":"minGuaranteedDays","in":"query","required":false,"description":"Minimum availability guarantee","schema":{"type":"integer","exclusiveMinimum":0}},{"name":"minNoticeHours","in":"query","required":false,"description":"Minimum notice before a machine is withdrawn","schema":{"type":"integer","exclusiveMinimum":0}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketplaceBrowse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Billing

Inspect costs and credits, configure auto-reload and create payments.

Billing is per organization and admin-only, with ?org= selecting it. Restricted keys also need billing scopes. Project-limited keys cannot run organization billing, except costs with an explicit permitted projectId, still as an admin. REST supports payments; MCP does not buy credits or change auto-reload.

## Current balance and billing status of the caller's organization

> Requires \`billing:read\` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Balance":{"type":"object","properties":{"balanceCents":{"type":"number","description":"Paid balance"},"promoBalanceCents":{"type":"number","description":"Promotional credit, spent first"},"currency":{"type":"string"},"overLimitCents":{"type":"number","description":"How far below zero the balance may go"},"status":{"type":["string","null"]},"mode":{"type":"string","description":"Billing mode of the organization"},"frozen":{"type":"boolean","description":"The organization is frozen: writes are refused until it is unfrozen"},"autoReload":{"$ref":"#/components/schemas/AutoReload"}},"required":["balanceCents","promoBalanceCents","currency","overLimitCents","status","mode","frozen","autoReload"],"additionalProperties":false},"AutoReload":{"type":"object","properties":{"enabled":{"type":"boolean","description":"The saved card is charged automatically when the balance drops below the threshold"},"thresholdCents":{"description":"Balance that triggers a reload","type":["number","null"]},"targetCents":{"description":"Balance after a reload","type":["number","null"]}},"required":["enabled","thresholdCents","targetCents"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/balance":{"get":{"operationId":"billing.balance","summary":"Current balance and billing status of the caller's organization","description":"Requires `billing:read` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Cost breakdown for a date range of up to 366 days. Default: the current calendar month, UTC

> Requires \`billing:read\` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"CostSummary":{"type":"object","properties":{"start":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"end":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"currency":{"type":"string"},"totalCost":{"type":"number","description":"Total for the range, in the billing currency"},"byProject":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string"},"projectName":{"type":"string"},"cost":{"type":"number"}},"required":["projectId","projectName","cost"],"additionalProperties":false}},"byApp":{"type":"array","items":{"type":"object","properties":{"appId":{"type":"string"},"appName":{"type":"string"},"projectId":{"type":"string"},"cost":{"type":"number"}},"required":["appId","appName","projectId","cost"],"additionalProperties":false}},"byResource":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"cost":{"type":"number"},"usage":{"type":"number"},"unit":{"type":"string"}},"required":["type","cost","usage","unit"],"additionalProperties":false},"description":"Cost per billed resource (CPU, GPU, RAM, storage, bandwidth, ...)"},"daily":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","description":"YYYY-MM-DD"},"cost":{"type":"number"}},"required":["date","cost"],"additionalProperties":false}}},"required":["start","end","currency","totalCost","byProject","byApp","byResource","daily"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/costs":{"get":{"operationId":"billing.costs","summary":"Cost breakdown for a date range of up to 366 days. Default: the current calendar month, UTC","description":"Requires `billing:read` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"start","in":"query","required":false,"description":"First day, YYYY-MM-DD","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"end","in":"query","required":false,"description":"Last day, YYYY-MM-DD","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"projectId","in":"query","required":false,"description":"Limit to one project","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}},{"name":"appId","in":"query","required":false,"description":"Limit to one app","schema":{"type":"string","pattern":"^[0-9a-fA-F]{24}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostSummary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Turn auto-reload on or off and set when the saved card is charged

> Requires \`billing:write\` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\
> \
> Errors specific to this operation:\
> \- 409 \`no\_payment\_method\`: No card is on file; the user adds one in the dashboard (billingUrl)\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"AutoReload":{"type":"object","properties":{"enabled":{"type":"boolean","description":"The saved card is charged automatically when the balance drops below the threshold"},"thresholdCents":{"description":"Balance that triggers a reload","type":["number","null"]},"targetCents":{"description":"Balance after a reload","type":["number","null"]}},"required":["enabled","thresholdCents","targetCents"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/auto-reload":{"patch":{"operationId":"billing.autoReload.update","summary":"Turn auto-reload on or off and set when the saved card is charged","description":"Requires `billing:write` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\n\nErrors specific to this operation:\n- 409 `no_payment_method`: No card is on file; the user adds one in the dashboard (billingUrl)\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Reload automatically when the balance drops below the threshold"},"thresholdCents":{"description":"Balance that triggers a reload, in US cents; required when enabling","type":"integer","minimum":500},"targetCents":{"description":"Balance after a reload, in US cents, 500 to 50000 above the threshold; required when enabling","type":"integer"}},"required":["enabled"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoReload"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Charge the saved card and add the amount to the balance

> Requires \`billing:write\` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\
> \
> Errors specific to this operation:\
> \- 400 \`amount\_too\_large\`: The amount exceeds the organization's per-transaction maximum (maxCents)\
> \- 409 \`no\_payment\_method\`: No card is on file; the user adds one in the dashboard (billingUrl)\
> \- 403 \`first\_payment\_in\_dashboard\`: The organization has never paid on-session; the first payment happens in the dashboard (billingUrl)\
> \- 429 \`topup\_limit\`: At most 10 API top-ups per day (resetAt)\
> \- 402 \`card\_declined\`: The issuer declined the charge (declineCode)\
> \- 402 \`authentication\_required\`: The issuer wants authentication, which needs a browser; pay in the dashboard (billingUrl)\
> \- 402 \`blocked\_by\_risk\`: The charge was blocked by fraud screening\
> \- 409 \`idempotency\_conflict\`: The idempotencyKey was used before with different parameters\
> \- 409 \`topup\_in\_progress\`: A charge with this idempotencyKey is still running\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Topup":{"type":"object","properties":{"id":{"type":"string","description":"Top-up id; also the Stripe payment intent id"},"status":{"type":"string","enum":["succeeded","processing","requires_action","failed"],"description":"Charge state. Credits are added once it is succeeded and the payment has been recorded"},"amountCents":{"type":"integer","description":"Amount charged, in US cents"},"credited":{"type":"boolean","description":"Whether the credits are on the balance yet"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","status","amountCents","credited","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PaymentRequired":{"description":"The balance or the card does not cover the action: insufficient_balance, balance_over_limit, card_declined, authentication_required or blocked_by_risk","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/topups":{"post":{"operationId":"billing.topups.create","summary":"Charge the saved card and add the amount to the balance","description":"Requires `billing:write` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\n\nErrors specific to this operation:\n- 400 `amount_too_large`: The amount exceeds the organization's per-transaction maximum (maxCents)\n- 409 `no_payment_method`: No card is on file; the user adds one in the dashboard (billingUrl)\n- 403 `first_payment_in_dashboard`: The organization has never paid on-session; the first payment happens in the dashboard (billingUrl)\n- 429 `topup_limit`: At most 10 API top-ups per day (resetAt)\n- 402 `card_declined`: The issuer declined the charge (declineCode)\n- 402 `authentication_required`: The issuer wants authentication, which needs a browser; pay in the dashboard (billingUrl)\n- 402 `blocked_by_risk`: The charge was blocked by fraud screening\n- 409 `idempotency_conflict`: The idempotencyKey was used before with different parameters\n- 409 `topup_in_progress`: A charge with this idempotencyKey is still running\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amountCents":{"type":"integer","minimum":500,"description":"Amount to charge, in US cents (at least 500)"},"idempotencyKey":{"description":"Client token; a retry with the same token returns the original charge instead of making a second one and does not count as another top-up for the day","type":"string","minLength":1,"maxLength":64}},"required":["amountCents"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Topup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## State of a top-up charged through the API

> Requires \`billing:read\` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Topup":{"type":"object","properties":{"id":{"type":"string","description":"Top-up id; also the Stripe payment intent id"},"status":{"type":"string","enum":["succeeded","processing","requires_action","failed"],"description":"Charge state. Credits are added once it is succeeded and the payment has been recorded"},"amountCents":{"type":"integer","description":"Amount charged, in US cents"},"credited":{"type":"boolean","description":"Whether the credits are on the balance yet"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","status","amountCents","credited","createdAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/topups/{topupId}":{"get":{"operationId":"billing.topups.get","summary":"State of a top-up charged through the API","description":"Requires `billing:read` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"topupId","in":"path","required":true,"description":"Top-up id returned when it was created","schema":{"type":"string","pattern":"^pi_[A-Za-z0-9]+$"}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Topup"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Create a hosted payment page for adding credits by card; nothing is charged until the user completes it

> Requires \`billing:write\` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\
> \
> Errors specific to this operation:\
> \- 400 \`amount\_too\_large\`: The amount exceeds the organization's per-transaction maximum (maxCents)\
> \- 409 \`no\_customer\`: The organization has no payment profile yet (billingUrl)\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"CheckoutSession":{"type":"object","properties":{"id":{"type":"string","description":"Checkout session id"},"url":{"type":"string","description":"Hosted payment page; the user opens it in a browser and pays there"},"amountCents":{"type":"integer","description":"Amount the page will charge, in US cents"},"expiresAt":{"type":"string","format":"date-time","description":"The link stops working after this time"}},"required":["id","url","amountCents","expiresAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/checkout-sessions":{"post":{"operationId":"billing.checkout.create","summary":"Create a hosted payment page for adding credits by card; nothing is charged until the user completes it","description":"Requires `billing:write` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\n\nErrors specific to this operation:\n- 400 `amount_too_large`: The amount exceeds the organization's per-transaction maximum (maxCents)\n- 409 `no_customer`: The organization has no payment profile yet (billingUrl)\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amountCents":{"type":"integer","minimum":500,"description":"Amount the page will charge, in US cents (at least 500)"},"successUrl":{"description":"Where the user lands after paying; defaults to the dashboard billing page","type":"string","format":"uri"},"cancelUrl":{"description":"Where the user lands after backing out; defaults to the dashboard billing page","type":"string","format":"uri"}},"required":["amountCents"]}}}},"responses":{"201":{"description":"Created","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSession"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## List the organization's monthly invoices, newest first. Optionally for one month, status or date range

> Requires \`billing:read\` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"Invoice":{"type":"object","properties":{"number":{"type":"string","description":"Invoice number; what the other invoice operations take"},"period":{"type":"object","properties":{"start":{"type":"string","description":"YYYY-MM-DD"},"end":{"type":"string","description":"YYYY-MM-DD"}},"required":["start","end"],"additionalProperties":false,"description":"Billing period, inclusive"},"status":{"type":"string","enum":["draft","finalized","paid"]},"partialMonth":{"type":"boolean","description":"The organization's billing began during the period"},"currency":{"type":"string"},"totalCents":{"type":"integer","description":"Usage charged in the period"},"amountDueCents":{"type":"integer","description":"What the organization owes on this invoice; 0 when credit covered it"},"closingBalanceCents":{"type":"integer","description":"Balance at the end of the period; negative means owed"},"createdOn":{"type":"string","description":"YYYY-MM-DD"},"updatedOn":{"anyOf":[{"type":"string","description":"YYYY-MM-DD"},{"type":"null"}]}},"required":["number","period","status","partialMonth","currency","totalCents","amountDueCents","closingBalanceCents","createdOn","updatedOn"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/invoices":{"get":{"operationId":"billing.invoices.list","summary":"List the organization's monthly invoices, newest first. Optionally for one month, status or date range","description":"Requires `billing:read` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"year","in":"query","required":false,"description":"Invoices of one year","schema":{"type":"integer","minimum":2020,"maximum":2100}},{"name":"month","in":"query","required":false,"description":"Invoices of one month (with year)","schema":{"type":"integer","minimum":1,"maximum":12}},{"name":"status","in":"query","required":false,"description":"Invoices in this state","schema":{"type":"string","enum":["draft","finalized","paid"]}},{"name":"from","in":"query","required":false,"description":"Billing periods starting on or after this day, YYYY-MM-DD","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Billing periods ending on or before this day, YYYY-MM-DD","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"invoices":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}}},"required":["invoices"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Get one invoice with its statement summary, itemized usage and the payments and credits of the period

> Requires \`billing:read\` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"InvoiceDetail":{"type":"object","properties":{"number":{"type":"string","description":"Invoice number; what the other invoice operations take"},"period":{"type":"object","properties":{"start":{"type":"string","description":"YYYY-MM-DD"},"end":{"type":"string","description":"YYYY-MM-DD"}},"required":["start","end"],"additionalProperties":false,"description":"Billing period, inclusive"},"status":{"type":"string","enum":["draft","finalized","paid"]},"partialMonth":{"type":"boolean","description":"The organization's billing began during the period"},"currency":{"type":"string"},"totalCents":{"type":"integer","description":"Usage charged in the period"},"amountDueCents":{"type":"integer","description":"What the organization owes on this invoice; 0 when credit covered it"},"closingBalanceCents":{"type":"integer","description":"Balance at the end of the period; negative means owed"},"createdOn":{"type":"string","description":"YYYY-MM-DD"},"updatedOn":{"anyOf":[{"type":"string","description":"YYYY-MM-DD"},{"type":"null"}]},"customer":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":["string","null"]},"billingEmail":{"type":["string","null"]}},"required":["email","name","billingEmail"],"additionalProperties":false},"statement":{"type":"object","properties":{"openingBalanceCents":{"type":"integer"},"closingBalanceCents":{"type":"integer"},"amountDueCents":{"type":"integer"},"paymentsCents":{"type":"integer","description":"Card and off-platform payments received in the period"},"promosCents":{"type":"integer","description":"Promotional credit granted in the period"},"refundsCents":{"type":"integer","description":"Signed"},"adjustmentsCents":{"type":"integer","description":"Signed"},"usageCents":{"type":"integer","description":"Usage charged in the period"},"taxCents":{"type":"integer","description":"Tax due on the amount owed; 0 when nothing is owed or no rate applies"},"taxRateBps":{"type":"integer","description":"Tax rate the invoice was billed under, in basis points"}},"required":["openingBalanceCents","closingBalanceCents","amountDueCents","paymentsCents","promosCents","refundsCents","adjustmentsCents","usageCents","taxCents","taxRateBps"],"additionalProperties":false,"description":"The statement summary: how the balance moved over the period"},"items":{"type":"array","items":{"type":"object","properties":{"resource":{"type":"string","description":"What was billed, e.g. a GPU model, RAM, storage, a server"},"detail":{"type":["string","null"]},"csp":{"type":["string","null"]},"region":{"type":["string","null"]},"zone":{"type":["string","null"]},"kcRegion":{"type":["string","null"]},"quantity":{"type":"number"},"unit":{"type":"string"},"unitPrice":{"type":"number","description":"Price per unit, in currency units (fractional)"},"amount":{"type":"number","description":"quantity times unitPrice, in currency units"}},"required":["resource","detail","csp","region","zone","kcRegion","quantity","unit","unitPrice","amount"],"additionalProperties":false},"description":"Itemized usage"},"credits":{"type":"array","items":{"type":"object","properties":{"ledgerEntryId":{"type":"string"},"type":{"type":"string","description":"Ledger entry type: credit_topup, manual_payment, a promo, refund, adjustment"},"at":{"type":"string","description":"ISO 8601 timestamp"},"amountCents":{"type":"integer","description":"Signed"},"label":{"type":"string"},"paymentMethod":{"anyOf":[{"type":"object","properties":{"brand":{"type":["string","null"]},"last4":{"type":["string","null"]}},"required":["brand","last4"],"additionalProperties":false},{"type":"null"}]}},"required":["ledgerEntryId","type","at","amountCents","label","paymentMethod"],"additionalProperties":false},"description":"Payments, promos, refunds and adjustments within the period"},"finalizedOn":{"anyOf":[{"type":"string","description":"YYYY-MM-DD"},{"type":"null"}]},"paidOn":{"anyOf":[{"type":"string","description":"YYYY-MM-DD"},{"type":"null"}]}},"required":["number","period","status","partialMonth","currency","totalCents","amountDueCents","closingBalanceCents","createdOn","updatedOn","customer","statement","items","credits","finalizedOn","paidOn"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/invoices/{invoiceNumber}":{"get":{"operationId":"billing.invoices.get","summary":"Get one invoice with its statement summary, itemized usage and the payments and credits of the period","description":"Requires `billing:read` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"invoiceNumber","in":"path","required":true,"description":"Invoice number","schema":{"type":"string","minLength":1}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceDetail"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Download an invoice as a PDF

> Requires \`billing:read\` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/billing/invoices/{invoiceNumber}/pdf":{"get":{"operationId":"billing.invoices.pdf","summary":"Download an invoice as a PDF","description":"Requires `billing:read` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"invoiceNumber","in":"path","required":true,"description":"Invoice number","schema":{"type":"string","minLength":1}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Usage charges summed per day or per hour, newest first. From the ledger, with the split between serverless apps, dedicated servers and spot leases

> Requires \`billing:read\` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"UsageCharge":{"type":"object","properties":{"period":{"type":"string","format":"date-time","description":"Start of the day or hour, in the requested time zone"},"amountCents":{"type":"number","description":"Usage charged in the period, including fractional cents"},"appsCents":{"type":"number","description":"Of amountCents, serverless app usage"},"serversCents":{"type":"number","description":"Of amountCents, dedicated servers"},"spotCents":{"type":"number","description":"Of amountCents, spot leases"},"currency":{"type":"string"}},"required":["period","amountCents","appsCents","serversCents","spotCents","currency"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/charges":{"get":{"operationId":"billing.charges.list","summary":"Usage charges summed per day or per hour, newest first. From the ledger, with the split between serverless apps, dedicated servers and spot leases","description":"Requires `billing:read` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"granularity","in":"query","required":false,"description":"Sum per day (default) or per hour","schema":{"type":"string","enum":["day","hour"]}},{"name":"from","in":"query","required":false,"description":"First day to include, YYYY-MM-DD","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Last day to include, YYYY-MM-DD","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"timeZone","in":"query","required":false,"description":"IANA time zone the days are counted in, e.g. America/Los_Angeles; UTC by default","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"charges":{"type":"array","items":{"$ref":"#/components/schemas/UsageCharge"}},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]}},"required":["charges","nextCursor"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Ledger entries that added or removed credit, newest first. Payments, promotional credit, refunds, adjustments

> Requires \`billing:read\` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"CreditEntry":{"type":"object","properties":{"id":{"type":"string","description":"Ledger entry id"},"at":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"type":{"type":"string","enum":["credit_topup","manual_payment","signup_promo","credit_promo","social_media_promo","refund","adjustment"],"description":"credit_topup and manual_payment are money in; the promos are granted credit"},"amountCents":{"type":"integer","description":"Signed: positive adds credit, negative removes it"},"currency":{"type":"string"},"status":{"type":"string","description":"succeeded, pending or failed"},"paymentMethod":{"anyOf":[{"type":"object","properties":{"brand":{"type":["string","null"]},"last4":{"type":["string","null"]}},"required":["brand","last4"],"additionalProperties":false},{"type":"null"}],"description":"The card charged, for card payments"},"note":{"description":"A note left by the person who recorded a manual entry","type":["string","null"]}},"required":["id","at","type","amountCents","currency","status","paymentMethod","note"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/credits":{"get":{"operationId":"billing.credits.list","summary":"Ledger entries that added or removed credit, newest first. Payments, promotional credit, refunds, adjustments","description":"Requires `billing:read` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}},{"name":"type","in":"query","required":false,"description":"Only entries of this type","schema":{"type":"string","enum":["credit_topup","manual_payment","signup_promo","credit_promo","social_media_promo","refund","adjustment"]}},{"name":"from","in":"query","required":false,"description":"First day to include, YYYY-MM-DD (UTC)","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"to","in":"query","required":false,"description":"Last day to include, YYYY-MM-DD (UTC)","schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"limit","in":"query","required":false,"description":"Items per page, 1 to 200 (default 50)","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"cursor","in":"query","required":false,"description":"nextCursor from the previous page; omit for the first page","schema":{"type":"string","minLength":1,"maxLength":512}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/CreditEntry"}},"nextCursor":{"description":"Pass as cursor to fetch the next page; null on the last page","type":["string","null"]}},"required":["entries","nextCursor"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## List the organization's saved cards and which one is the default

> Requires \`billing:read\` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"PaymentMethod":{"type":"object","properties":{"id":{"type":"string","description":"Payment method id"},"brand":{"description":"Card brand, e.g. visa","type":["string","null"]},"last4":{"type":["string","null"]},"expMonth":{"anyOf":[{"type":"integer"},{"type":"null"}]},"expYear":{"anyOf":[{"type":"integer"},{"type":"null"}]},"default":{"type":"boolean","description":"The card auto-reload and top-ups charge"},"addedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","brand","last4","expMonth","expYear","default","addedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/payment-methods":{"get":{"operationId":"billing.paymentMethods.list","summary":"List the organization's saved cards and which one is the default","description":"Requires `billing:read` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"paymentMethods":{"type":"array","items":{"$ref":"#/components/schemas/PaymentMethod"}}},"required":["paymentMethods"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Make a saved card the default for auto-reload and top-ups

> Requires \`billing:write\` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\
> \
> Errors specific to this operation:\
> \- 404 \`not\_found\`: No saved card has this id\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"PaymentMethod":{"type":"object","properties":{"id":{"type":"string","description":"Payment method id"},"brand":{"description":"Card brand, e.g. visa","type":["string","null"]},"last4":{"type":["string","null"]},"expMonth":{"anyOf":[{"type":"integer"},{"type":"null"}]},"expYear":{"anyOf":[{"type":"integer"},{"type":"null"}]},"default":{"type":"boolean","description":"The card auto-reload and top-ups charge"},"addedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","brand","last4","expMonth","expYear","default","addedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/payment-methods/{paymentMethodId}/default":{"post":{"operationId":"billing.paymentMethods.setDefault","summary":"Make a saved card the default for auto-reload and top-ups","description":"Requires `billing:write` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\n\nErrors specific to this operation:\n- 404 `not_found`: No saved card has this id\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"paymentMethodId","in":"path","required":true,"description":"Payment method id from the list","schema":{"type":"string","minLength":1}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"paymentMethods":{"type":"array","items":{"$ref":"#/components/schemas/PaymentMethod"}}},"required":["paymentMethods"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Remove a saved card. A removed default is replaced by the next saved card; auto-reload is switched off when no card is left.

> Requires \`billing:write\` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\
> \
> Errors specific to this operation:\
> \- 404 \`not\_found\`: No saved card has this id\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"PaymentMethod":{"type":"object","properties":{"id":{"type":"string","description":"Payment method id"},"brand":{"description":"Card brand, e.g. visa","type":["string","null"]},"last4":{"type":["string","null"]},"expMonth":{"anyOf":[{"type":"integer"},{"type":"null"}]},"expYear":{"anyOf":[{"type":"integer"},{"type":"null"}]},"default":{"type":"boolean","description":"The card auto-reload and top-ups charge"},"addedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","brand","last4","expMonth","expYear","default","addedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/payment-methods/{paymentMethodId}":{"delete":{"operationId":"billing.paymentMethods.remove","summary":"Remove a saved card. A removed default is replaced by the next saved card; auto-reload is switched off when no card is left.","description":"Requires `billing:write` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\n\nErrors specific to this operation:\n- 404 `not_found`: No saved card has this id\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"paymentMethodId","in":"path","required":true,"description":"Payment method id from the list","schema":{"type":"string","minLength":1}},{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"paymentMethods":{"type":"array","items":{"$ref":"#/components/schemas/PaymentMethod"},"description":"The cards that remain"},"autoReloadDisabled":{"type":"boolean","description":"Auto-reload was switched off because no card is left"}},"required":["paymentMethods","autoReloadDisabled"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Redeem a voucher code for organization credit; once per person per code

> Requires \`billing:write\` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\
> \
> Errors specific to this operation:\
> \- 404 \`voucher\_not\_found\`: No voucher has this code\
> \- 400 \`voucher\_rejected\`: The voucher is inactive, used up, not yet valid, or restricted to other accounts\
> \- 429 \`too\_many\_attempts\`: More than five codes tried in a minute\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}},"paths":{"/billing/vouchers/redeem":{"post":{"operationId":"billing.vouchers.redeem","summary":"Redeem a voucher code for organization credit; once per person per code","description":"Requires `billing:write` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\n\nErrors specific to this operation:\n- 404 `voucher_not_found`: No voucher has this code\n- 400 `voucher_rejected`: The voucher is inactive, used up, not yet valid, or restricted to other accounts\n- 429 `too_many_attempts`: More than five codes tried in a minute\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":64,"description":"The voucher code"}},"required":["code"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"amountCents":{"type":"integer","description":"Credit added to the balance"},"redemptionsRemaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Uses the voucher has left"}},"required":["amountCents","redemptionsRemaining"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Read the organization's node protection setting. Whether its dedicated servers are hibernated automatically when the balance is about to run out, and at how many hours of runway

> Requires \`billing:read\` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"NodeProtection":{"type":"object","properties":{"enabled":{"type":"boolean","description":"The organization's dedicated servers are hibernated when the balance runs low"},"thresholdHours":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Hours of runway left (balance over spend rate) at which the servers are hibernated; null when off"},"hibernatedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When protection last hibernated the servers"},"deprovisionedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When a balance past its limit last deprovisioned them"}},"required":["enabled","thresholdHours","hibernatedAt","deprovisionedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/node-protection":{"get":{"operationId":"billing.nodeProtection.get","summary":"Read the organization's node protection setting. Whether its dedicated servers are hibernated automatically when the balance is about to run out, and at how many hours of runway","description":"Requires `billing:read` (View balance, invoices, charges, credits, payment methods and cost breakdowns).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeProtection"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```

## Turn node protection on with a runway threshold, or off. On: when the balance would run out within thresholdHours at the current spend rate, the organization's dedicated servers are hibernated (storage only) until credits are added.

> Requires \`billing:write\` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\
> \
> Errors specific to this operation:\
> \- 400 \`org\_required\`: The caller reaches more than one organization and named none; details.orgs lists their slugs\
> \- 403 \`key\_project\_restricted\`: The key is limited to specific projects, and this acts on the organization

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Billing"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"NodeProtection":{"type":"object","properties":{"enabled":{"type":"boolean","description":"The organization's dedicated servers are hibernated when the balance runs low"},"thresholdHours":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Hours of runway left (balance over spend rate) at which the servers are hibernated; null when off"},"hibernatedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When protection last hibernated the servers"},"deprovisionedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When a balance past its limit last deprovisioned them"}},"required":["enabled","thresholdHours","hibernatedAt","deprovisionedAt"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The resource is in a state that does not allow the action, or an idempotency key was reused","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PayloadTooLarge":{"description":"The request body exceeds the limit given in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/billing/node-protection":{"patch":{"operationId":"billing.nodeProtection.update","summary":"Turn node protection on with a runway threshold, or off. On: when the balance would run out within thresholdHours at the current spend rate, the organization's dedicated servers are hibernated (storage only) until credits are added.","description":"Requires `billing:write` (Change auto-reload, payment methods and node protection, redeem vouchers, add credits from the saved card).\n\nErrors specific to this operation:\n- 400 `org_required`: The caller reaches more than one organization and named none; details.orgs lists their slugs\n- 403 `key_project_restricted`: The key is limited to specific projects, and this acts on the organization","tags":["Billing"],"parameters":[{"name":"org","in":"query","required":false,"description":"Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one","schema":{"type":"string","minLength":3,"maxLength":39}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Turn protection on or off"},"thresholdHours":{"description":"Hours of runway that trigger hibernation, 2 to 48; required when enabling","type":"integer","minimum":2,"maximum":48}},"required":["enabled"]}}}},"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeProtection"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Docs

Retrieve the built-in platform guides used by agents.

Agent guides cover getting\_started, deploy, operate, troubleshoot, servers, billing, organizations, sharing and errors. The same topics are MCP read\_docs actions and kinesis\://docs resources.

## Read the platform guide for agents: how to deploy, operate and troubleshoot apps, servers and billing

> Any API key can call this.

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"tags":[{"name":"Docs"}],"servers":[{"url":"https://api.kinesis.network/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from Account settings, e.g. kn_..."}},"headers":{"RequestId":{"description":"Id of this request; quote it when contacting support","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests per minute allowed for this key, or for the user across all keys, whichever is lower","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window","schema":{"type":"integer"}},"RateLimitReset":{"description":"Unix time in seconds when the window resets","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"GuideDocument":{"type":"object","properties":{"topic":{"type":"string","enum":["getting_started","deploy","operate","troubleshoot","servers","billing","organizations","sharing","errors"],"description":"getting_started (concepts, ids, permissions), deploy (create_app fields), operate (updates, lifecycle, logs), troubleshoot (states and failures), servers (catalog, provisioning, grids), sharing (grantees, invitations), billing, errors (every code)"},"title":{"type":"string"},"markdown":{"type":"string","description":"The guide text, in markdown"}},"required":["topic","title","markdown"],"additionalProperties":false},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}},"responses":{"BadRequest":{"description":"The request body, query or headers are invalid (invalid_request, invalid_idempotency_key, ...)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"The API key is missing, malformed, unknown, revoked or expired","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key lacks a required permission (insufficient_scope), is limited to other projects or to projects while the call acts on the organization (key_project_restricted), the caller's role on the resource is too low (forbidden), or the account is frozen","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or the caller cannot see it","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests; see the X-RateLimit-* headers and Retry-After","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServerError":{"description":"Something failed on our side; the request id is in the body","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"A service we depend on (the runtime, GitHub) did not answer; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unavailable":{"description":"The feature is not enabled on this server (github_not_configured), or a service we depend on is down; retry later","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"GatewayTimeout":{"description":"A service we depend on did not answer in time; retry shortly","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/docs/{topic}":{"get":{"operationId":"docs.get","summary":"Read the platform guide for agents: how to deploy, operate and troubleshoot apps, servers and billing","description":"Any API key can call this.","tags":["Docs"],"parameters":[{"name":"topic","in":"path","required":true,"description":"getting_started (concepts, ids, permissions), deploy (create_app fields), operate (updates, lifecycle, logs), troubleshoot (states and failures), servers (catalog, provisioning, grids), sharing (grantees, invitations), billing, errors (every code)","schema":{"type":"string","enum":["getting_started","deploy","operate","troubleshoot","servers","billing","organizations","sharing","errors"]}}],"responses":{"200":{"description":"OK","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuideDocument"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"502":{"$ref":"#/components/responses/UpstreamError"},"503":{"$ref":"#/components/responses/Unavailable"},"504":{"$ref":"#/components/responses/GatewayTimeout"}}}}}}
```


# Models

Explore the response objects returned by the Kinesis API.

Response models for OpenAPI 1.5.0. Resource creator identities are null unless the credential has members:read and the person belongs to the resource’s organization. Organization roles use admin, developer and viewer; resource grants retain editor and viewer.

## The GuideDocument object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"GuideDocument":{"type":"object","properties":{"topic":{"type":"string","enum":["getting_started","deploy","operate","troubleshoot","servers","billing","organizations","sharing","errors"],"description":"getting_started (concepts, ids, permissions), deploy (create_app fields), operate (updates, lifecycle, logs), troubleshoot (states and failures), servers (catalog, provisioning, grids), sharing (grantees, invitations), billing, errors (every code)"},"title":{"type":"string"},"markdown":{"type":"string","description":"The guide text, in markdown"}},"required":["topic","title","markdown"],"additionalProperties":false}}}}
```

## The Identity object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Identity":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"}},"required":["id","email","name"],"additionalProperties":false},"limitedToOrgs":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"}},"required":["id","slug","name"],"additionalProperties":false}},{"type":"null"}],"description":"The organizations the key or connection is limited to; null when it reaches every one the user is in, including ones they join later (orgs.list has them, with the user's role in each)"},"apiKey":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"},"description":"Granted permissions; [\"*\"] is full access, including permissions added to the API later"},"projectIds":{"type":"array","items":{"type":"string"},"description":"Projects the key is limited to; empty means every project"}},"required":["id","name","scopes","projectIds"],"additionalProperties":false},{"type":"null"}],"description":"The key in use, or null when the caller is not using an API key"},"oauthClient":{"anyOf":[{"type":"object","properties":{"grantId":{"type":"string","description":"The connection as listed in the dashboard's connected apps"},"clientId":{"type":"string"},"clientName":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"},"description":"Permissions this connection was authorized with"}},"required":["grantId","clientId","clientName","scopes"],"additionalProperties":false},{"type":"null"}],"description":"The app connected through OAuth (hosted chat connectors), or null"}},"required":["user","limitedToOrgs","apiKey","oauthClient"],"additionalProperties":false}}}}
```

## The Notification object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Notification":{"type":"object","properties":{"id":{"type":"string","description":"Notification id"},"type":{"type":"string","description":"What happened, e.g. share_invitation_received, grid_machines_online"},"title":{"type":"string"},"message":{"type":["string","null"]},"resourceType":{"anyOf":[{"type":"string","enum":["project","app","grid"]},{"type":"null"}]},"resourceId":{"type":["string","null"]},"url":{"description":"Dashboard page the notification points at","type":["string","null"]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"seenAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"resolved":{"type":"boolean","description":"Its inline action (accept, decline) has been taken already"}},"required":["id","type","title","message","resourceType","resourceId","url","createdAt","seenAt","resolved"],"additionalProperties":false}}}}
```

## The Organization object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Organization":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","description":"What calls in an organization take as org"},"name":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role there, which bounds what they can do in it"}},"required":["id","slug","name","role"],"additionalProperties":false}}}}
```

## The OrganizationDetail object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"OrganizationDetail":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string","description":"What calls in an organization take as org"},"name":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role there, which bounds what they can do in it"},"isOwner":{"type":"boolean","description":"The caller owns it (members.list names the owner)"},"settings":{"type":"object","properties":{"serverlessEnabled":{"type":"boolean","description":"Developers may run apps on Kinesis serverless capacity, billed to the organization; admins always can"},"membersCanInvite":{"type":"boolean","description":"Developers may invite people, as developers or viewers; admins always can"}},"required":["serverlessEnabled","membersCanInvite"],"additionalProperties":false},"frozen":{"type":"boolean","description":"Frozen over a payment problem: it can't start apps, add servers or pay"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","slug","name","role","isOwner","settings","frozen","createdAt"],"additionalProperties":false}}}}
```

## The OrgInvitation object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"OrgInvitation":{"type":"object","properties":{"id":{"type":"string","description":"Invitation id"},"org":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The role the caller would join with"},"invitedBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}]},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","org","role","invitedBy","expiresAt"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false}}}}
```

## The Person object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false}}}}
```

## The AuditEntry object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AuditEntry":{"type":"object","properties":{"id":{"type":"string"},"at":{"type":"string","format":"date-time","description":"When it happened"},"actor":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who did it; null for the platform, or an account that is gone"},"viaSupport":{"type":"boolean","description":"Kinesis support did it while signed in as the actor"},"role":{"description":"The actor's role at the time","type":["string","null"]},"resourceType":{"type":"string","description":"project, app, pool (a grid) or org (its members, settings and servers)"},"resourceId":{"description":"Project or app id, grid id, or the organization's id","type":["string","null"]},"resourceName":{"description":"The project's or app's name, while it exists","type":["string","null"]},"action":{"type":"string","description":"What was done, e.g. appCreate, appStop, memberInvite, serverProvision"},"outcome":{"type":"string","enum":["success","error"]},"errorCode":{"description":"Why it failed, as a code; the stored message can name internals","type":["string","null"]},"details":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"What the action concerned: an app's name, a member's email and role, server addresses, ..."},"changes":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The field, e.g. name or envVars.API_KEY"},"kind":{"type":"string","enum":["added","removed","changed"]},"before":{},"after":{},"redacted":{"description":"A secret: its values are not kept","type":"boolean"}},"required":["path","kind"],"additionalProperties":false},"description":"Field-level changes, for actions that edit fields"},"message":{"description":"For something the platform did: what it reported","type":["string","null"]}},"required":["id","at","actor","viaSupport","role","resourceType","resourceId","resourceName","action","outcome","errorCode","details","changes","message"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false}}}}
```

## The Member object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Member":{"type":"object","properties":{"userId":{"type":"string","description":"What add_grantees and the member calls take"},"email":{"type":"string"},"name":{"type":["string","null"]},"role":{"type":"string","enum":["admin","developer","viewer"]},"joinedAt":{"type":"string"},"isOwner":{"type":"boolean","description":"Owns the organization: always an admin, and stays until they transfer ownership"},"lastSignInAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"With details, for admins: when they last signed in"},"created":{"anyOf":[{"type":"object","properties":{"projects":{"type":"integer"},"apps":{"type":"integer"},"grids":{"type":"integer"}},"required":["projects","apps","grids"],"additionalProperties":false},{"type":"null"}],"description":"With details, for admins: what they created here, which stays when they leave"}},"required":["userId","email","name","role","joinedAt","isOwner","lastSignInAt","created"],"additionalProperties":false}}}}
```

## The MemberInvitation object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"MemberInvitation":{"type":"object","properties":{"id":{"type":"string","description":"Invitation id"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The role they join with"},"invitedBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}]},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"expired":{"type":"boolean","description":"Past its date; inviting the address again sends a fresh link"},"addsTo":{"type":"array","items":{"type":"string"},"description":"Names of the projects, apps and grids they are added to on joining"}},"required":["id","email","role","invitedBy","expiresAt","expired","addsTo"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false}}}}
```

## The Project object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Project":{"type":"object","properties":{"id":{"type":"string","description":"Project id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"access":{"$ref":"#/components/schemas/Access"},"appCount":{"type":"integer"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","name","org","visibility","access","appCount","createdBy","createdAt"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false}}}}
```

## The OrgRef object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"}}}}
```

## The Access object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false}}}}
```

## The ProjectDetail object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"ProjectDetail":{"type":"object","properties":{"id":{"type":"string","description":"Project id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"access":{"$ref":"#/components/schemas/Access"},"appCount":{"type":"integer"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"apps":{"type":"array","items":{"$ref":"#/components/schemas/App"}}},"required":["id","name","org","visibility","access","appCount","createdBy","createdAt","apps"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"App":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt"],"additionalProperties":false}}}}
```

## The App object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"App":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt"],"additionalProperties":false},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false}}}}
```

## The Placement object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Placement":{"type":"object","properties":{"placeable":{"type":"boolean","description":"A server can take the app now, a hibernated one would fit once woken, or one can be provisioned"},"serversWithCapacity":{"type":"integer","description":"Servers that match and have room now"},"serversWithoutCapacity":{"type":"integer","description":"Servers that match but are full"},"wakeable":{"type":"integer","description":"Hibernated servers that would match once woken (power_servers wake, or the automatic wake-up on start)"},"wakeableAwaitingStock":{"type":"integer","description":"Of wakeable, servers whose provider has no stock right now, so a wake would not land yet"},"provisionable":{"type":"boolean","description":"The platform can provision a matching cloud machine on demand for a serverless app"},"rejections":{"type":"array","items":{"type":"object","properties":{"server":{"type":"string","description":"Server address"},"reason":{"type":"string"}},"required":["server","reason"],"additionalProperties":false},"description":"Why sampled servers cannot take the app; each distinct reason appears first, then repeats"}},"required":["placeable","serversWithCapacity","serversWithoutCapacity","wakeable","wakeableAwaitingStock","provisionable","rejections"],"additionalProperties":false}}}}
```

## The AppCreateResult object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppCreateResult":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"status":{"description":"Status as last cached; use the status endpoint for a live read","$ref":"#/components/schemas/AppStatus"},"config":{"anyOf":[{"$ref":"#/components/schemas/AppConfig"},{"type":"null"}]},"github":{"anyOf":[{"$ref":"#/components/schemas/AppGitHubSource"},{"type":"null"}],"description":"Repository settings of a GitHub-sourced app"},"defaultDomain":{"description":"Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one","type":["string","null"]},"start":{"description":"How the automatic start after creation went","$ref":"#/components/schemas/AppStartOutcome"},"buildId":{"description":"First build of a GitHub-sourced app; null for image apps","type":["string","null"]}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt","status","config","github","defaultDomain","start","buildId"],"additionalProperties":false},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"AppStatus":{"type":"object","properties":{"state":{"type":"string","enum":["unknown","running","stopped","starting","stopping","scaling","updating","downloading","provisioning","start_blocked","error","building","build_failed"],"description":"Aggregate state; build states win while there is no runnable image"},"targetState":{"type":"string","enum":["running","stopped","unknown"],"description":"What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"},"nodes":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"state":{"type":"string"},"reportedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the server last reported this state; not a start time"}},"required":["address","state","reportedAt"],"additionalProperties":false},"description":"Servers the app is placed on and the container state on each"},"endpoints":{"type":"array","items":{"$ref":"#/components/schemas/AppEndpoint"},"description":"Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"},"errors":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"message":{"type":"string"},"at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"details":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"Extra data the runtime attached, such as the grid or server involved"}},"required":["type","message","at","details"],"additionalProperties":false},"description":"Recent runtime errors, newest first"},"workOrders":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"What the runtime asked the server to do"},"server":{"type":"string","description":"Server address"},"status":{"type":"string"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["type","server","status","createdAt","startedAt","completedAt"],"additionalProperties":false},"description":"The runtime's latest instructions to servers, newest first; an order that never completes is the stall"},"grid":{"anyOf":[{"type":"object","properties":{"fill":{"anyOf":[{"type":"object","properties":{"target":{"type":"integer","description":"Members the grid is composed to have"},"online":{"type":"integer","description":"Members the app runs on"},"pending":{"type":"integer"},"converging":{"type":"boolean","description":"The grid is still acquiring members or placing the app"}},"required":["target","online","pending","converging"],"additionalProperties":false},{"type":"null"}],"description":"How far the grid has filled for this app"},"idleReclaim":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"warnedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"evictAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the app becomes evictable; null when the grid only warns"},"evictedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"cooldownEndsAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the grid takes the app again after an eviction"},"evidence":{"type":"string","description":"Why the grid considers the app idle"}},"required":["gridId","warnedAt","evictAt","evictedAt","cooldownEndsAt","evidence"],"additionalProperties":false},"description":"Grids whose idle policy has warned or evicted the app"},"queue":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"position":{"type":"integer","description":"1-based place in the grid's fair-share queue"},"queueLength":{"type":"integer"},"enqueuedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"held":{"type":"boolean","description":"The scheduler keeps the app off the grid until its turn comes"}},"required":["gridId","position","queueLength","enqueuedAt","held"],"additionalProperties":false},"description":"Full grids the app is queued on"}},"required":["fill","idleReclaim","queue"],"additionalProperties":false},{"type":"null"}],"description":"Grid placement details; null for a serverless app"},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the status again while it is changing; null in a settled state"}},"required":["state","targetState","nodes","endpoints","errors","workOrders","grid","pollAfterSec"],"additionalProperties":false},"AppEndpoint":{"type":"object","properties":{"port":{"type":"integer","description":"Container port, as in config.ports"},"protocol":{"type":"string","description":"Protocol of that port, as in config.ports"},"address":{"description":"An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired","type":["string","null"]},"server":{"description":"Server address of a direct tcp or udp endpoint; null when load-balanced","type":["string","null"]}},"required":["port","protocol","address","server"],"additionalProperties":false},"AppConfig":{"type":"object","properties":{"image":{"type":"string","description":"Container image reference"},"imageTag":{"description":"Tag, kept separately for Docker Hub images","type":["string","null"]},"registryUsername":{"description":"Registry login; the password is write-only","type":["string","null"]},"ports":{"type":"array","items":{"type":"object","properties":{"port":{"type":"integer"},"protocol":{"type":"string"}},"required":["port","protocol"],"additionalProperties":false}},"env":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":["string","null"]},"secret":{"type":"boolean"}},"required":["key","value","secret"],"additionalProperties":false},"description":"Environment variables; value is null for secrets, and on a connection that takes no credentials for values that look like one (null keeps a value on update)"},"command":{"type":"array","items":{"type":"string"}},"entrypoint":{"type":"array","items":{"type":"string"}},"startupCommand":{"anyOf":[{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"user":{"type":"string"},"timeoutSec":{"type":"number"},"startPeriodSec":{"type":"number"}},"required":["args","user","timeoutSec","startPeriodSec"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"anyOf":[{"type":"object","properties":{"test":{"type":"array","items":{"type":"string"}},"intervalSec":{"type":"number"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"anyOf":[{"type":"object","properties":{"mode":{"type":"string"},"startTimeoutSec":{"type":"number"},"maxRetriesPerNode":{"type":"number"},"maxErrorTolerance":{"type":"number"},"numTasks":{"type":"number"}},"required":["mode","startTimeoutSec","maxRetriesPerNode","maxErrorTolerance","numTasks"],"additionalProperties":false},{"type":"null"}]},"instances":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"}},"required":["min","max"],"additionalProperties":false},"stateful":{"type":"boolean"},"hardware":{"type":"object","properties":{"gpuCount":{"type":"integer"},"gpuModels":{"type":"array","items":{"type":"string"}},"gpuManufacturer":{"type":["string","null"]},"gpuVramBytes":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"shmemBytes":{"type":"number"}},"required":["gpuCount","gpuModels","gpuManufacturer","gpuVramBytes","ramBytes","storageBytes","shmemBytes"],"additionalProperties":false},"placement":{"type":"object","properties":{"regions":{"type":"array","items":{"type":"string"},"description":"Kinesis regions, e.g. us-west"},"providers":{"type":"array","items":{"type":"object","properties":{"csp":{"type":"string"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string"},"zones":{"type":"array","items":{"type":"string"}}},"required":["region","zones"],"additionalProperties":false}}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"type":"array","items":{"type":"string"},"description":"Source IPs or CIDR ranges allowed to reach the app"}},"required":["regions","providers","allowedIps"],"additionalProperties":false},"grids":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}},"priorities":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"Priority per grid id"}},"required":["ids","priorities"],"additionalProperties":false},"volumes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"target":{"type":"string"},"declaredSizeBytes":{"type":"number"}},"required":["name","target","declaredSizeBytes"],"additionalProperties":false}},"domains":{"type":"array","items":{"type":"string"},"description":"Custom domains attached to the app (see the domain operations)"}},"required":["image","imageTag","registryUsername","ports","env","command","entrypoint","startupCommand","healthcheck","restartPolicy","instances","stateful","hardware","placement","grids","volumes","domains"],"additionalProperties":false},"AppGitHubSource":{"type":"object","properties":{"installationId":{"type":"integer","description":"GitHub App installation the repository is read through"},"repo":{"type":"string","description":"owner/name"},"url":{"type":"string"},"branch":{"type":"string","description":"Branch that is built and, with autoDeploy, followed"},"contextDir":{"type":"string"},"dockerfilePath":{"description":"Dockerfile path relative to contextDir; not used while dockerfileContent is set","type":["string","null"]},"dockerfileOverride":{"type":"boolean","description":"An inline Dockerfile (dockerfileContent) is built instead of dockerfilePath"},"dockerfileContent":{"description":"The inline Dockerfile that is built; null when the repository's own file is used","type":["string","null"]},"autoDeploy":{"type":"boolean","description":"Every push to the branch rebuilds and redeploys"},"clonedFrom":{"description":"Public repository (owner/name) the app's repository was copied from at creation; null otherwise","type":["string","null"]}},"required":["installationId","repo","url","branch","contextDir","dockerfilePath","dockerfileOverride","dockerfileContent","autoDeploy","clonedFrom"],"additionalProperties":false},"AppStartOutcome":{"type":"object","properties":{"state":{"type":"string","enum":["requested","deferred","building","failed"],"description":"requested: the start was accepted, poll the status to follow it; deferred: machines are being provisioned for the app first, poll the status; building: the app starts once its first build succeeds, poll the build; failed: the app exists but is stopped, see error"},"error":{"description":"Why the automatic start failed","type":["string","null"]}},"required":["state","error"],"additionalProperties":false}}}}
```

## The AppEndpoint object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppEndpoint":{"type":"object","properties":{"port":{"type":"integer","description":"Container port, as in config.ports"},"protocol":{"type":"string","description":"Protocol of that port, as in config.ports"},"address":{"description":"An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired","type":["string","null"]},"server":{"description":"Server address of a direct tcp or udp endpoint; null when load-balanced","type":["string","null"]}},"required":["port","protocol","address","server"],"additionalProperties":false}}}}
```

## The AppStatus object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppStatus":{"type":"object","properties":{"state":{"type":"string","enum":["unknown","running","stopped","starting","stopping","scaling","updating","downloading","provisioning","start_blocked","error","building","build_failed"],"description":"Aggregate state; build states win while there is no runnable image"},"targetState":{"type":"string","enum":["running","stopped","unknown"],"description":"What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"},"nodes":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"state":{"type":"string"},"reportedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the server last reported this state; not a start time"}},"required":["address","state","reportedAt"],"additionalProperties":false},"description":"Servers the app is placed on and the container state on each"},"endpoints":{"type":"array","items":{"$ref":"#/components/schemas/AppEndpoint"},"description":"Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"},"errors":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"message":{"type":"string"},"at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"details":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"Extra data the runtime attached, such as the grid or server involved"}},"required":["type","message","at","details"],"additionalProperties":false},"description":"Recent runtime errors, newest first"},"workOrders":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"What the runtime asked the server to do"},"server":{"type":"string","description":"Server address"},"status":{"type":"string"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["type","server","status","createdAt","startedAt","completedAt"],"additionalProperties":false},"description":"The runtime's latest instructions to servers, newest first; an order that never completes is the stall"},"grid":{"anyOf":[{"type":"object","properties":{"fill":{"anyOf":[{"type":"object","properties":{"target":{"type":"integer","description":"Members the grid is composed to have"},"online":{"type":"integer","description":"Members the app runs on"},"pending":{"type":"integer"},"converging":{"type":"boolean","description":"The grid is still acquiring members or placing the app"}},"required":["target","online","pending","converging"],"additionalProperties":false},{"type":"null"}],"description":"How far the grid has filled for this app"},"idleReclaim":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"warnedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"evictAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the app becomes evictable; null when the grid only warns"},"evictedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"cooldownEndsAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the grid takes the app again after an eviction"},"evidence":{"type":"string","description":"Why the grid considers the app idle"}},"required":["gridId","warnedAt","evictAt","evictedAt","cooldownEndsAt","evidence"],"additionalProperties":false},"description":"Grids whose idle policy has warned or evicted the app"},"queue":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"position":{"type":"integer","description":"1-based place in the grid's fair-share queue"},"queueLength":{"type":"integer"},"enqueuedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"held":{"type":"boolean","description":"The scheduler keeps the app off the grid until its turn comes"}},"required":["gridId","position","queueLength","enqueuedAt","held"],"additionalProperties":false},"description":"Full grids the app is queued on"}},"required":["fill","idleReclaim","queue"],"additionalProperties":false},{"type":"null"}],"description":"Grid placement details; null for a serverless app"},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the status again while it is changing; null in a settled state"}},"required":["state","targetState","nodes","endpoints","errors","workOrders","grid","pollAfterSec"],"additionalProperties":false},"AppEndpoint":{"type":"object","properties":{"port":{"type":"integer","description":"Container port, as in config.ports"},"protocol":{"type":"string","description":"Protocol of that port, as in config.ports"},"address":{"description":"An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired","type":["string","null"]},"server":{"description":"Server address of a direct tcp or udp endpoint; null when load-balanced","type":["string","null"]}},"required":["port","protocol","address","server"],"additionalProperties":false}}}}
```

## The AppConfig object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppConfig":{"type":"object","properties":{"image":{"type":"string","description":"Container image reference"},"imageTag":{"description":"Tag, kept separately for Docker Hub images","type":["string","null"]},"registryUsername":{"description":"Registry login; the password is write-only","type":["string","null"]},"ports":{"type":"array","items":{"type":"object","properties":{"port":{"type":"integer"},"protocol":{"type":"string"}},"required":["port","protocol"],"additionalProperties":false}},"env":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":["string","null"]},"secret":{"type":"boolean"}},"required":["key","value","secret"],"additionalProperties":false},"description":"Environment variables; value is null for secrets, and on a connection that takes no credentials for values that look like one (null keeps a value on update)"},"command":{"type":"array","items":{"type":"string"}},"entrypoint":{"type":"array","items":{"type":"string"}},"startupCommand":{"anyOf":[{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"user":{"type":"string"},"timeoutSec":{"type":"number"},"startPeriodSec":{"type":"number"}},"required":["args","user","timeoutSec","startPeriodSec"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"anyOf":[{"type":"object","properties":{"test":{"type":"array","items":{"type":"string"}},"intervalSec":{"type":"number"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"anyOf":[{"type":"object","properties":{"mode":{"type":"string"},"startTimeoutSec":{"type":"number"},"maxRetriesPerNode":{"type":"number"},"maxErrorTolerance":{"type":"number"},"numTasks":{"type":"number"}},"required":["mode","startTimeoutSec","maxRetriesPerNode","maxErrorTolerance","numTasks"],"additionalProperties":false},{"type":"null"}]},"instances":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"}},"required":["min","max"],"additionalProperties":false},"stateful":{"type":"boolean"},"hardware":{"type":"object","properties":{"gpuCount":{"type":"integer"},"gpuModels":{"type":"array","items":{"type":"string"}},"gpuManufacturer":{"type":["string","null"]},"gpuVramBytes":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"shmemBytes":{"type":"number"}},"required":["gpuCount","gpuModels","gpuManufacturer","gpuVramBytes","ramBytes","storageBytes","shmemBytes"],"additionalProperties":false},"placement":{"type":"object","properties":{"regions":{"type":"array","items":{"type":"string"},"description":"Kinesis regions, e.g. us-west"},"providers":{"type":"array","items":{"type":"object","properties":{"csp":{"type":"string"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string"},"zones":{"type":"array","items":{"type":"string"}}},"required":["region","zones"],"additionalProperties":false}}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"type":"array","items":{"type":"string"},"description":"Source IPs or CIDR ranges allowed to reach the app"}},"required":["regions","providers","allowedIps"],"additionalProperties":false},"grids":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}},"priorities":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"Priority per grid id"}},"required":["ids","priorities"],"additionalProperties":false},"volumes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"target":{"type":"string"},"declaredSizeBytes":{"type":"number"}},"required":["name","target","declaredSizeBytes"],"additionalProperties":false}},"domains":{"type":"array","items":{"type":"string"},"description":"Custom domains attached to the app (see the domain operations)"}},"required":["image","imageTag","registryUsername","ports","env","command","entrypoint","startupCommand","healthcheck","restartPolicy","instances","stateful","hardware","placement","grids","volumes","domains"],"additionalProperties":false}}}}
```

## The AppGitHubSource object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppGitHubSource":{"type":"object","properties":{"installationId":{"type":"integer","description":"GitHub App installation the repository is read through"},"repo":{"type":"string","description":"owner/name"},"url":{"type":"string"},"branch":{"type":"string","description":"Branch that is built and, with autoDeploy, followed"},"contextDir":{"type":"string"},"dockerfilePath":{"description":"Dockerfile path relative to contextDir; not used while dockerfileContent is set","type":["string","null"]},"dockerfileOverride":{"type":"boolean","description":"An inline Dockerfile (dockerfileContent) is built instead of dockerfilePath"},"dockerfileContent":{"description":"The inline Dockerfile that is built; null when the repository's own file is used","type":["string","null"]},"autoDeploy":{"type":"boolean","description":"Every push to the branch rebuilds and redeploys"},"clonedFrom":{"description":"Public repository (owner/name) the app's repository was copied from at creation; null otherwise","type":["string","null"]}},"required":["installationId","repo","url","branch","contextDir","dockerfilePath","dockerfileOverride","dockerfileContent","autoDeploy","clonedFrom"],"additionalProperties":false}}}}
```

## The AppStartOutcome object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppStartOutcome":{"type":"object","properties":{"state":{"type":"string","enum":["requested","deferred","building","failed"],"description":"requested: the start was accepted, poll the status to follow it; deferred: machines are being provisioned for the app first, poll the status; building: the app starts once its first build succeeds, poll the build; failed: the app exists but is stopped, see error"},"error":{"description":"Why the automatic start failed","type":["string","null"]}},"required":["state","error"],"additionalProperties":false}}}}
```

## The AppDetail object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppDetail":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"status":{"description":"Status as last cached; use the status endpoint for a live read","$ref":"#/components/schemas/AppStatus"},"config":{"anyOf":[{"$ref":"#/components/schemas/AppConfig"},{"type":"null"}]},"github":{"anyOf":[{"$ref":"#/components/schemas/AppGitHubSource"},{"type":"null"}],"description":"Repository settings of a GitHub-sourced app"},"defaultDomain":{"description":"Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one","type":["string","null"]}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt","status","config","github","defaultDomain"],"additionalProperties":false},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"AppStatus":{"type":"object","properties":{"state":{"type":"string","enum":["unknown","running","stopped","starting","stopping","scaling","updating","downloading","provisioning","start_blocked","error","building","build_failed"],"description":"Aggregate state; build states win while there is no runnable image"},"targetState":{"type":"string","enum":["running","stopped","unknown"],"description":"What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"},"nodes":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"state":{"type":"string"},"reportedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the server last reported this state; not a start time"}},"required":["address","state","reportedAt"],"additionalProperties":false},"description":"Servers the app is placed on and the container state on each"},"endpoints":{"type":"array","items":{"$ref":"#/components/schemas/AppEndpoint"},"description":"Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"},"errors":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"message":{"type":"string"},"at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"details":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"Extra data the runtime attached, such as the grid or server involved"}},"required":["type","message","at","details"],"additionalProperties":false},"description":"Recent runtime errors, newest first"},"workOrders":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"What the runtime asked the server to do"},"server":{"type":"string","description":"Server address"},"status":{"type":"string"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["type","server","status","createdAt","startedAt","completedAt"],"additionalProperties":false},"description":"The runtime's latest instructions to servers, newest first; an order that never completes is the stall"},"grid":{"anyOf":[{"type":"object","properties":{"fill":{"anyOf":[{"type":"object","properties":{"target":{"type":"integer","description":"Members the grid is composed to have"},"online":{"type":"integer","description":"Members the app runs on"},"pending":{"type":"integer"},"converging":{"type":"boolean","description":"The grid is still acquiring members or placing the app"}},"required":["target","online","pending","converging"],"additionalProperties":false},{"type":"null"}],"description":"How far the grid has filled for this app"},"idleReclaim":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"warnedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"evictAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the app becomes evictable; null when the grid only warns"},"evictedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"cooldownEndsAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the grid takes the app again after an eviction"},"evidence":{"type":"string","description":"Why the grid considers the app idle"}},"required":["gridId","warnedAt","evictAt","evictedAt","cooldownEndsAt","evidence"],"additionalProperties":false},"description":"Grids whose idle policy has warned or evicted the app"},"queue":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"position":{"type":"integer","description":"1-based place in the grid's fair-share queue"},"queueLength":{"type":"integer"},"enqueuedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"held":{"type":"boolean","description":"The scheduler keeps the app off the grid until its turn comes"}},"required":["gridId","position","queueLength","enqueuedAt","held"],"additionalProperties":false},"description":"Full grids the app is queued on"}},"required":["fill","idleReclaim","queue"],"additionalProperties":false},{"type":"null"}],"description":"Grid placement details; null for a serverless app"},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the status again while it is changing; null in a settled state"}},"required":["state","targetState","nodes","endpoints","errors","workOrders","grid","pollAfterSec"],"additionalProperties":false},"AppEndpoint":{"type":"object","properties":{"port":{"type":"integer","description":"Container port, as in config.ports"},"protocol":{"type":"string","description":"Protocol of that port, as in config.ports"},"address":{"description":"An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired","type":["string","null"]},"server":{"description":"Server address of a direct tcp or udp endpoint; null when load-balanced","type":["string","null"]}},"required":["port","protocol","address","server"],"additionalProperties":false},"AppConfig":{"type":"object","properties":{"image":{"type":"string","description":"Container image reference"},"imageTag":{"description":"Tag, kept separately for Docker Hub images","type":["string","null"]},"registryUsername":{"description":"Registry login; the password is write-only","type":["string","null"]},"ports":{"type":"array","items":{"type":"object","properties":{"port":{"type":"integer"},"protocol":{"type":"string"}},"required":["port","protocol"],"additionalProperties":false}},"env":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":["string","null"]},"secret":{"type":"boolean"}},"required":["key","value","secret"],"additionalProperties":false},"description":"Environment variables; value is null for secrets, and on a connection that takes no credentials for values that look like one (null keeps a value on update)"},"command":{"type":"array","items":{"type":"string"}},"entrypoint":{"type":"array","items":{"type":"string"}},"startupCommand":{"anyOf":[{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"user":{"type":"string"},"timeoutSec":{"type":"number"},"startPeriodSec":{"type":"number"}},"required":["args","user","timeoutSec","startPeriodSec"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"anyOf":[{"type":"object","properties":{"test":{"type":"array","items":{"type":"string"}},"intervalSec":{"type":"number"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"anyOf":[{"type":"object","properties":{"mode":{"type":"string"},"startTimeoutSec":{"type":"number"},"maxRetriesPerNode":{"type":"number"},"maxErrorTolerance":{"type":"number"},"numTasks":{"type":"number"}},"required":["mode","startTimeoutSec","maxRetriesPerNode","maxErrorTolerance","numTasks"],"additionalProperties":false},{"type":"null"}]},"instances":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"}},"required":["min","max"],"additionalProperties":false},"stateful":{"type":"boolean"},"hardware":{"type":"object","properties":{"gpuCount":{"type":"integer"},"gpuModels":{"type":"array","items":{"type":"string"}},"gpuManufacturer":{"type":["string","null"]},"gpuVramBytes":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"shmemBytes":{"type":"number"}},"required":["gpuCount","gpuModels","gpuManufacturer","gpuVramBytes","ramBytes","storageBytes","shmemBytes"],"additionalProperties":false},"placement":{"type":"object","properties":{"regions":{"type":"array","items":{"type":"string"},"description":"Kinesis regions, e.g. us-west"},"providers":{"type":"array","items":{"type":"object","properties":{"csp":{"type":"string"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string"},"zones":{"type":"array","items":{"type":"string"}}},"required":["region","zones"],"additionalProperties":false}}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"type":"array","items":{"type":"string"},"description":"Source IPs or CIDR ranges allowed to reach the app"}},"required":["regions","providers","allowedIps"],"additionalProperties":false},"grids":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}},"priorities":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"Priority per grid id"}},"required":["ids","priorities"],"additionalProperties":false},"volumes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"target":{"type":"string"},"declaredSizeBytes":{"type":"number"}},"required":["name","target","declaredSizeBytes"],"additionalProperties":false}},"domains":{"type":"array","items":{"type":"string"},"description":"Custom domains attached to the app (see the domain operations)"}},"required":["image","imageTag","registryUsername","ports","env","command","entrypoint","startupCommand","healthcheck","restartPolicy","instances","stateful","hardware","placement","grids","volumes","domains"],"additionalProperties":false},"AppGitHubSource":{"type":"object","properties":{"installationId":{"type":"integer","description":"GitHub App installation the repository is read through"},"repo":{"type":"string","description":"owner/name"},"url":{"type":"string"},"branch":{"type":"string","description":"Branch that is built and, with autoDeploy, followed"},"contextDir":{"type":"string"},"dockerfilePath":{"description":"Dockerfile path relative to contextDir; not used while dockerfileContent is set","type":["string","null"]},"dockerfileOverride":{"type":"boolean","description":"An inline Dockerfile (dockerfileContent) is built instead of dockerfilePath"},"dockerfileContent":{"description":"The inline Dockerfile that is built; null when the repository's own file is used","type":["string","null"]},"autoDeploy":{"type":"boolean","description":"Every push to the branch rebuilds and redeploys"},"clonedFrom":{"description":"Public repository (owner/name) the app's repository was copied from at creation; null otherwise","type":["string","null"]}},"required":["installationId","repo","url","branch","contextDir","dockerfilePath","dockerfileOverride","dockerfileContent","autoDeploy","clonedFrom"],"additionalProperties":false}}}}
```

## The AppUpdateResult object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppUpdateResult":{"type":"object","properties":{"id":{"type":"string","description":"App id"},"projectId":{"type":"string"},"name":{"type":"string"},"guid":{"type":"string","description":"Runtime identifier of the app"},"source":{"description":"Where the image comes from: Docker Hub, a registry link, GitHub or an upload","type":["string","null"]},"image":{"type":["string","null"]},"domain":{"description":"Hostname the app answers on: the default custom domain, else the Kinesis-issued one","type":["string","null"]},"buildState":{"description":"Build pipeline state for GitHub-sourced apps","type":["string","null"]},"access":{"$ref":"#/components/schemas/Access"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"status":{"description":"Status as last cached; use the status endpoint for a live read","$ref":"#/components/schemas/AppStatus"},"config":{"anyOf":[{"$ref":"#/components/schemas/AppConfig"},{"type":"null"}]},"github":{"anyOf":[{"$ref":"#/components/schemas/AppGitHubSource"},{"type":"null"}],"description":"Repository settings of a GitHub-sourced app"},"defaultDomain":{"description":"Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one","type":["string","null"]},"buildId":{"description":"Build started by a source change; null when nothing was rebuilt","type":["string","null"]}},"required":["id","projectId","name","guid","source","image","domain","buildState","access","createdBy","createdAt","status","config","github","defaultDomain","buildId"],"additionalProperties":false},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false},"AppStatus":{"type":"object","properties":{"state":{"type":"string","enum":["unknown","running","stopped","starting","stopping","scaling","updating","downloading","provisioning","start_blocked","error","building","build_failed"],"description":"Aggregate state; build states win while there is no runnable image"},"targetState":{"type":"string","enum":["running","stopped","unknown"],"description":"What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"},"nodes":{"type":"array","items":{"type":"object","properties":{"address":{"type":"string"},"state":{"type":"string"},"reportedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the server last reported this state; not a start time"}},"required":["address","state","reportedAt"],"additionalProperties":false},"description":"Servers the app is placed on and the container state on each"},"endpoints":{"type":"array","items":{"$ref":"#/components/schemas/AppEndpoint"},"description":"Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"},"errors":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"message":{"type":"string"},"at":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"details":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"Extra data the runtime attached, such as the grid or server involved"}},"required":["type","message","at","details"],"additionalProperties":false},"description":"Recent runtime errors, newest first"},"workOrders":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","description":"What the runtime asked the server to do"},"server":{"type":"string","description":"Server address"},"status":{"type":"string"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["type","server","status","createdAt","startedAt","completedAt"],"additionalProperties":false},"description":"The runtime's latest instructions to servers, newest first; an order that never completes is the stall"},"grid":{"anyOf":[{"type":"object","properties":{"fill":{"anyOf":[{"type":"object","properties":{"target":{"type":"integer","description":"Members the grid is composed to have"},"online":{"type":"integer","description":"Members the app runs on"},"pending":{"type":"integer"},"converging":{"type":"boolean","description":"The grid is still acquiring members or placing the app"}},"required":["target","online","pending","converging"],"additionalProperties":false},{"type":"null"}],"description":"How far the grid has filled for this app"},"idleReclaim":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"warnedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"evictAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the app becomes evictable; null when the grid only warns"},"evictedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"cooldownEndsAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When the grid takes the app again after an eviction"},"evidence":{"type":"string","description":"Why the grid considers the app idle"}},"required":["gridId","warnedAt","evictAt","evictedAt","cooldownEndsAt","evidence"],"additionalProperties":false},"description":"Grids whose idle policy has warned or evicted the app"},"queue":{"type":"array","items":{"type":"object","properties":{"gridId":{"type":"string"},"position":{"type":"integer","description":"1-based place in the grid's fair-share queue"},"queueLength":{"type":"integer"},"enqueuedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"held":{"type":"boolean","description":"The scheduler keeps the app off the grid until its turn comes"}},"required":["gridId","position","queueLength","enqueuedAt","held"],"additionalProperties":false},"description":"Full grids the app is queued on"}},"required":["fill","idleReclaim","queue"],"additionalProperties":false},{"type":"null"}],"description":"Grid placement details; null for a serverless app"},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the status again while it is changing; null in a settled state"}},"required":["state","targetState","nodes","endpoints","errors","workOrders","grid","pollAfterSec"],"additionalProperties":false},"AppEndpoint":{"type":"object","properties":{"port":{"type":"integer","description":"Container port, as in config.ports"},"protocol":{"type":"string","description":"Protocol of that port, as in config.ports"},"address":{"description":"An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired","type":["string","null"]},"server":{"description":"Server address of a direct tcp or udp endpoint; null when load-balanced","type":["string","null"]}},"required":["port","protocol","address","server"],"additionalProperties":false},"AppConfig":{"type":"object","properties":{"image":{"type":"string","description":"Container image reference"},"imageTag":{"description":"Tag, kept separately for Docker Hub images","type":["string","null"]},"registryUsername":{"description":"Registry login; the password is write-only","type":["string","null"]},"ports":{"type":"array","items":{"type":"object","properties":{"port":{"type":"integer"},"protocol":{"type":"string"}},"required":["port","protocol"],"additionalProperties":false}},"env":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":["string","null"]},"secret":{"type":"boolean"}},"required":["key","value","secret"],"additionalProperties":false},"description":"Environment variables; value is null for secrets, and on a connection that takes no credentials for values that look like one (null keeps a value on update)"},"command":{"type":"array","items":{"type":"string"}},"entrypoint":{"type":"array","items":{"type":"string"}},"startupCommand":{"anyOf":[{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}},"user":{"type":"string"},"timeoutSec":{"type":"number"},"startPeriodSec":{"type":"number"}},"required":["args","user","timeoutSec","startPeriodSec"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"anyOf":[{"type":"object","properties":{"test":{"type":"array","items":{"type":"string"}},"intervalSec":{"type":"number"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"anyOf":[{"type":"object","properties":{"mode":{"type":"string"},"startTimeoutSec":{"type":"number"},"maxRetriesPerNode":{"type":"number"},"maxErrorTolerance":{"type":"number"},"numTasks":{"type":"number"}},"required":["mode","startTimeoutSec","maxRetriesPerNode","maxErrorTolerance","numTasks"],"additionalProperties":false},{"type":"null"}]},"instances":{"type":"object","properties":{"min":{"type":"integer"},"max":{"type":"integer"}},"required":["min","max"],"additionalProperties":false},"stateful":{"type":"boolean"},"hardware":{"type":"object","properties":{"gpuCount":{"type":"integer"},"gpuModels":{"type":"array","items":{"type":"string"}},"gpuManufacturer":{"type":["string","null"]},"gpuVramBytes":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"shmemBytes":{"type":"number"}},"required":["gpuCount","gpuModels","gpuManufacturer","gpuVramBytes","ramBytes","storageBytes","shmemBytes"],"additionalProperties":false},"placement":{"type":"object","properties":{"regions":{"type":"array","items":{"type":"string"},"description":"Kinesis regions, e.g. us-west"},"providers":{"type":"array","items":{"type":"object","properties":{"csp":{"type":"string"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string"},"zones":{"type":"array","items":{"type":"string"}}},"required":["region","zones"],"additionalProperties":false}}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"type":"array","items":{"type":"string"},"description":"Source IPs or CIDR ranges allowed to reach the app"}},"required":["regions","providers","allowedIps"],"additionalProperties":false},"grids":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"}},"priorities":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"},"description":"Priority per grid id"}},"required":["ids","priorities"],"additionalProperties":false},"volumes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"target":{"type":"string"},"declaredSizeBytes":{"type":"number"}},"required":["name","target","declaredSizeBytes"],"additionalProperties":false}},"domains":{"type":"array","items":{"type":"string"},"description":"Custom domains attached to the app (see the domain operations)"}},"required":["image","imageTag","registryUsername","ports","env","command","entrypoint","startupCommand","healthcheck","restartPolicy","instances","stateful","hardware","placement","grids","volumes","domains"],"additionalProperties":false},"AppGitHubSource":{"type":"object","properties":{"installationId":{"type":"integer","description":"GitHub App installation the repository is read through"},"repo":{"type":"string","description":"owner/name"},"url":{"type":"string"},"branch":{"type":"string","description":"Branch that is built and, with autoDeploy, followed"},"contextDir":{"type":"string"},"dockerfilePath":{"description":"Dockerfile path relative to contextDir; not used while dockerfileContent is set","type":["string","null"]},"dockerfileOverride":{"type":"boolean","description":"An inline Dockerfile (dockerfileContent) is built instead of dockerfilePath"},"dockerfileContent":{"description":"The inline Dockerfile that is built; null when the repository's own file is used","type":["string","null"]},"autoDeploy":{"type":"boolean","description":"Every push to the branch rebuilds and redeploys"},"clonedFrom":{"description":"Public repository (owner/name) the app's repository was copied from at creation; null otherwise","type":["string","null"]}},"required":["installationId","repo","url","branch","contextDir","dockerfilePath","dockerfileOverride","dockerfileContent","autoDeploy","clonedFrom"],"additionalProperties":false}}}}
```

## The AppLogs object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppLogs":{"type":"object","properties":{"logs":{"type":"array","items":{"type":"object","properties":{"node":{"type":"string","description":"Server address the lines came from"},"from":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"to":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"lines":{"type":"array","items":{"type":"string"}}},"required":["node","from","to","lines"],"additionalProperties":false},"description":"Chunks in chronological order, consecutive lines from one server merged into a chunk that from and to span; the last line of the last chunk is the newest"},"nextCursorNs":{"description":"Pass as cursorNs to fetch the next page in the same direction; null at the end","type":["string","null"]},"hasMore":{"type":"boolean"},"totalLines":{"type":"integer","description":"Lines in this page across every chunk"}},"required":["logs","nextCursorNs","hasMore","totalLines"],"additionalProperties":false}}}}
```

## The AppUsage object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppUsage":{"type":"object","properties":{"start":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"end":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"intervalSeconds":{"type":"integer","description":"Width of each bucket"},"series":{"type":"array","items":{"type":"object","properties":{"node":{"type":"string","description":"Server address"},"points":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time","description":"Bucket start"},"cpuNs":{"type":"number","description":"CPU time consumed, in nanoseconds"},"gpuNs":{"type":"number","description":"GPU time consumed, in nanoseconds"},"gpuMemBytesSecs":{"type":"number","description":"GPU memory held, in byte-seconds"},"memBytesSecs":{"type":"number","description":"RAM held, in byte-seconds"},"storageBytesSecs":{"type":"number","description":"Disk held, in byte-seconds"},"logStorageBytesSecs":{"type":"number","description":"Log storage held, in byte-seconds"},"bwInBytes":{"type":"number","description":"Bytes received"},"bwOutBytes":{"type":"number","description":"Bytes sent"}},"required":["at","cpuNs","gpuNs","gpuMemBytesSecs","memBytesSecs","storageBytesSecs","logStorageBytesSecs","bwInBytes","bwOutBytes"],"additionalProperties":false}}},"required":["node","points"],"additionalProperties":false},"description":"One series per server the app ran on; each point sums the bucket"}},"required":["start","end","intervalSeconds","series"],"additionalProperties":false}}}}
```

## The LogRetention object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"LogRetention":{"type":"object","properties":{"days":{"type":"integer","description":"How many days of container logs are kept"},"isDefault":{"type":"boolean","description":"The platform default applies; no per-app setting"}},"required":["days","isDefault"],"additionalProperties":false}}}}
```

## The Build object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Build":{"type":"object","properties":{"id":{"type":"string","description":"Build id"},"appId":{"type":"string"},"status":{"type":"string","enum":["pending","running","succeeded","failed","cancelled"],"description":"running until the image is built and the app deployed"},"step":{"type":"string","enum":["vcs_import","image_build","app_deploy","completed"],"description":"Pipeline step in progress, or completed"},"imageBuildStatus":{"description":"The image builder's own status (Created, Running, Completed, Failed, Canceled, TimedOut) once that step started","type":["string","null"]},"source":{"anyOf":[{"type":"object","properties":{"repo":{"type":["string","null"]},"branch":{"type":["string","null"]},"commitSha":{"type":["string","null"]},"commitMessage":{"type":["string","null"]},"contextDir":{"type":["string","null"]},"dockerfilePath":{"type":["string","null"]}},"required":["repo","branch","commitSha","commitMessage","contextDir","dockerfilePath"],"additionalProperties":false},{"type":"null"}],"description":"What was built; the commit is filled in once the build starts"},"error":{"anyOf":[{"type":"object","properties":{"step":{"type":"string","description":"Step the build failed at"},"message":{"type":"string"},"code":{"type":["string","null"]}},"required":["step","message","code"],"additionalProperties":false},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"startedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"finishedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"pollAfterSec":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Seconds to wait before reading the build again; null once it has finished"}},"required":["id","appId","status","step","imageBuildStatus","source","error","createdAt","startedAt","finishedAt","pollAfterSec"],"additionalProperties":false}}}}
```

## The BuildLogs object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"BuildLogs":{"type":"object","properties":{"id":{"type":"string","description":"Build id"},"status":{"type":"string","enum":["pending","running","succeeded","failed","cancelled"]},"imageBuildStatus":{"type":["string","null"]},"lines":{"type":"array","items":{"type":"string"},"description":"Builder output, oldest first"},"complete":{"type":"boolean","description":"The image build has finished, so the log is final"},"truncated":{"type":"boolean","description":"Older lines were dropped to keep the last 2000"}},"required":["id","status","imageBuildStatus","lines","complete","truncated"],"additionalProperties":false}}}}
```

## The DnsRecord object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"DnsRecord":{"type":"object","properties":{"type":{"type":"string","description":"Record type, e.g. CNAME or NS"},"name":{"type":"string","description":"Record name to create"},"value":{"type":"string","description":"Record value"}},"required":["type","name","value"],"additionalProperties":false}}}}
```

## The GitHubAccount object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"GitHubAccount":{"type":"object","properties":{"installationId":{"type":"integer","description":"Installation id to pass to the other GitHub operations"},"login":{"type":"string","description":"GitHub user or organization name"},"type":{"type":"string","enum":["user","organization"]},"avatarUrl":{"type":["string","null"]}},"required":["installationId","login","type","avatarUrl"],"additionalProperties":false}}}}
```

## The GitHubRepo object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"GitHubRepo":{"type":"object","properties":{"id":{"type":"integer","description":"GitHub's numeric repository id"},"name":{"type":"string","description":"owner/name"},"defaultBranch":{"type":"string"},"private":{"type":"boolean"},"url":{"type":"string"},"description":{"type":["string","null"]},"language":{"type":["string","null"]},"updatedAt":{"type":"string","description":"Last push or change, ISO 8601"}},"required":["id","name","defaultBranch","private","url","description","language","updatedAt"],"additionalProperties":false}}}}
```

## The GitHubCommit object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"GitHubCommit":{"type":"object","properties":{"sha":{"type":"string"},"message":{"type":"string","description":"First line of the commit message"},"author":{"type":"string","description":"GitHub login of the author; empty when unknown"},"committedAt":{"type":"string","description":"ISO 8601; empty when unknown"}},"required":["sha","message","author","committedAt"],"additionalProperties":false}}}}
```

## The GitHubEntry object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"GitHubEntry":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string","description":"Relative to the repository root"},"type":{"type":"string","enum":["file","dir","symlink","submodule"]},"size":{"type":"integer","description":"Bytes; 0 for a directory"}},"required":["name","path","type","size"],"additionalProperties":false}}}}
```

## The AppTemplate object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AppTemplate":{"type":"object","properties":{"id":{"type":"string","description":"Template id"},"name":{"type":"string","description":"Suggested app name"},"category":{"type":"string","enum":["llm_general","llm_performance","general"]},"image":{"description":"Container image the template deploys","type":["string","null"]},"paidTierOnly":{"type":"boolean","description":"Meant for organizations with a payment on record"},"notes":{"description":"What to fill in before deploying, when the template is not complete as is","type":["string","null"]},"config":{"type":"object","properties":{"image":{"description":"Container image reference, e.g. nginx:1.27 or ghcr.io/org/app:tag","type":"string","minLength":1,"maxLength":512},"registryAuth":{"description":"Credentials for a private image","type":"object","properties":{"username":{"type":"string","minLength":1,"description":"Registry login"},"password":{"type":"string","minLength":1,"description":"Registry password or token (write-only)"}},"required":["username","password"],"additionalProperties":false},"ports":{"description":"Ports to expose","maxItems":32,"type":"array","items":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535,"description":"Container port"},"protocol":{"type":"string","enum":["tcp","udp","tcp_load_balancer","tcps_load_balancer","http_load_balancer","https_load_balancer","http80_load_balancer","https443_load_balancer","grpcs_load_balancer"],"description":"How the port is exposed"}},"required":["port","protocol"],"additionalProperties":false}},"env":{"description":"Environment variables","maxItems":200,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]*$","description":"Variable name"},"value":{"description":"Value; null keeps the stored value of an existing variable","type":["string","null"]},"secret":{"description":"Store as a secret: never returned by reads","type":"boolean"}},"required":["key","value"],"additionalProperties":false}},"command":{"description":"Container command (CMD) override","maxItems":256,"type":"array","items":{"type":"string"}},"entrypoint":{"description":"Container entrypoint override","maxItems":256,"type":"array","items":{"type":"string"}},"startupCommand":{"description":"Command run inside the container once it starts; null clears it","anyOf":[{"type":"object","properties":{"args":{"minItems":1,"type":"array","items":{"type":"string"},"description":"Command and arguments"},"user":{"description":"User to run as","type":"string"},"timeoutSec":{"description":"Seconds before the command is considered failed","type":"integer","minimum":0},"startPeriodSec":{"description":"Seconds to wait after the container starts","type":"integer","minimum":0}},"required":["args"],"additionalProperties":false},{"type":"null"}]},"healthcheck":{"description":"Container health check; null clears it","anyOf":[{"type":"object","properties":{"test":{"minItems":1,"type":"array","items":{"type":"string"},"description":"Command that must exit 0 for the app to count as healthy"},"intervalSec":{"type":"integer","minimum":1,"description":"Seconds between checks"}},"required":["test","intervalSec"],"additionalProperties":false},{"type":"null"}]},"restartPolicy":{"description":"Restart behaviour","type":"object","properties":{"mode":{"type":"string","enum":["disabled","always","on_failure","task_completion"],"description":"When to restart a container that exits"},"startTimeoutSec":{"description":"Seconds a start may take before it counts as failed","type":"integer","minimum":0},"maxRetriesPerNode":{"description":"Restarts allowed on one server before moving on","type":"integer","minimum":0},"maxErrorTolerance":{"description":"Failures tolerated before the app is stopped","type":"integer","minimum":0},"numTasks":{"description":"For task_completion: runs to complete before stopping","type":"integer","minimum":0}},"required":["mode"],"additionalProperties":false},"instances":{"description":"Instance count range","type":"object","properties":{"min":{"type":"integer","minimum":0,"description":"Instances to keep running"},"max":{"type":"integer","minimum":0,"description":"Upper bound for scaling"}},"required":["min","max"],"additionalProperties":false},"stateful":{"description":"Keep each instance pinned to its server and disk","type":"boolean"},"hardware":{"description":"Hardware requirements per instance","type":"object","properties":{"gpuCount":{"description":"GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only","type":"integer","minimum":0,"maximum":16},"gpuModels":{"description":"Acceptable GPU models, e.g. H100; empty means any","maxItems":16,"type":"array","items":{"type":"string","minLength":1}},"gpuManufacturer":{"description":"GPU vendor; null means any","anyOf":[{"type":"string","enum":["amd","apple","intel","nvidia","xilinx"]},{"type":"null"}]},"gpuVramBytes":{"description":"Minimum VRAM per GPU, in bytes","type":"integer","minimum":0},"ramBytes":{"description":"RAM per instance, in bytes","type":"integer","exclusiveMinimum":0},"storageBytes":{"description":"Disk per instance, in bytes","type":"integer","exclusiveMinimum":0},"shmemBytes":{"description":"Shared memory (/dev/shm) size, in bytes","type":"integer","minimum":0}},"additionalProperties":false},"placement":{"description":"Where the app may run and who may reach it","type":"object","properties":{"regions":{"description":"Kinesis regions to run in; empty means any","type":"array","items":{"type":"string","enum":["us-west","us-east","us-central","ca-west","ca-east","ca-central","eu-west","eu-north","eu-east","eu-south","eu-central","eu-south-east","ap-east","ap-south-east","ap-north-east","ap-south"]}},"providers":{"description":"Restrict placement to specific providers and regions","type":"array","items":{"type":"object","properties":{"csp":{"type":"string","enum":["aws","azure","gcp","kinesis"],"description":"Cloud provider"},"regions":{"type":"array","items":{"type":"object","properties":{"region":{"type":"string","minLength":1,"description":"Provider region"},"zones":{"type":"array","items":{"type":"string"},"description":"Zones within the region; empty means any"}},"required":["region","zones"],"additionalProperties":false},"description":"Regions of the provider to allow"}},"required":["csp","regions"],"additionalProperties":false}},"allowedIps":{"description":"Source IPs or CIDR ranges allowed to reach the app; empty means everyone","maxItems":100,"type":"array","items":{"type":"string"}}},"additionalProperties":false},"grids":{"description":"Run on grids instead of serverless capacity (members only, on grids they may deploy to)","type":"object","properties":{"ids":{"description":"Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"priorities":{"description":"Priority per grid id, -100 to 100","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"integer","minimum":-100,"maximum":100}}},"additionalProperties":false},"volumes":{"description":"Persistent volumes","maxItems":4,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$","description":"Volume name, unique within the app"},"target":{"type":"string","pattern":"^\\/.+","description":"Absolute mount path inside the container"},"declaredSizeBytes":{"description":"Expected size, used for placement; 0 or omitted means unspecified","type":"integer","minimum":0,"maximum":17592186044416}},"required":["name","target"],"additionalProperties":false}}},"additionalProperties":false,"description":"Pass as config to apps.create, edited as needed"}},"required":["id","name","category","image","paidTierOnly","notes","config"],"additionalProperties":false}}}}
```

## The GranteeList object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"GranteeList":{"type":"object","properties":{"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string"},"resourceName":{"type":"string"},"callerRole":{"type":"string","enum":["viewer","editor","owner"],"description":"Legacy permission values: owner means an organization admin; editor means the member who created the resource"},"grantees":{"type":"array","items":{"$ref":"#/components/schemas/Grantee"}}},"required":["resourceType","resourceId","resourceName","callerRole","grantees"],"additionalProperties":false},"Grantee":{"type":"object","properties":{"userId":{"type":"string","description":"User id; what update and revoke take"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string","enum":["viewer","editor"],"description":"Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"},"orgRole":{"anyOf":[{"type":"string","enum":["admin","developer","viewer"]},{"type":"null"}],"description":"Their role in the resource's organization; null for someone outside it"},"grantedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["userId","email","name","role","orgRole","grantedAt"],"additionalProperties":false}}}}
```

## The Grantee object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Grantee":{"type":"object","properties":{"userId":{"type":"string","description":"User id; what update and revoke take"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"role":{"type":"string","enum":["viewer","editor"],"description":"Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"},"orgRole":{"anyOf":[{"type":"string","enum":["admin","developer","viewer"]},{"type":"null"}],"description":"Their role in the resource's organization; null for someone outside it"},"grantedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["userId","email","name","role","orgRole","grantedAt"],"additionalProperties":false}}}}
```

## The Invitation object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Invitation":{"type":"object","properties":{"id":{"type":"string","description":"Invitation id"},"resourceType":{"type":"string","enum":["project","app","grid"],"description":"What is shared"},"resourceId":{"type":"string"},"resourceName":{"type":"string"},"inviteeEmail":{"type":"string"},"invitedByEmail":{"type":"string","description":"Who sent it"},"role":{"type":"string","enum":["viewer","editor"],"description":"Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"},"status":{"type":"string","enum":["pending","accepted","rejected"]},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"acceptedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["id","resourceType","resourceId","resourceName","inviteeEmail","invitedByEmail","role","status","createdAt","acceptedAt"],"additionalProperties":false}}}}
```

## The Grid object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Grid":{"type":"object","properties":{"id":{"type":"string","description":"Grid id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"access":{"$ref":"#/components/schemas/Access"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"nodes":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers in the grid"},"autoCreated":{"type":"boolean","description":"Created by the platform rather than the user"},"priorityMode":{"type":"string","enum":["absolute","weighted"],"description":"How app priorities apply when the grid is contended"},"needsReconciliation":{"type":"boolean","description":"Membership changes are still being applied"},"volumes":{"type":"array","items":{"type":"object","properties":{"mountPath":{"type":"string"},"maxSize":{"type":"number"},"tag":{"type":"string"}},"required":["mountPath","maxSize","tag"],"additionalProperties":false}},"networks":{"type":"array","items":{"type":"object","properties":{"cidr":{"type":"string"},"tag":{"type":"string"}},"required":["cidr","tag"],"additionalProperties":false}}},"required":["id","name","org","access","visibility","createdBy","nodes","autoCreated","priorityMode","needsReconciliation","volumes","networks"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false}}}}
```

## The GridCreateResult object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"GridCreateResult":{"type":"object","properties":{"id":{"type":"string","description":"Grid id"},"name":{"type":"string"},"org":{"$ref":"#/components/schemas/OrgRef"},"access":{"$ref":"#/components/schemas/Access"},"visibility":{"type":"string","enum":["private","org"],"description":"private: admins, its creator and the people added; org: everyone in the organization"},"createdBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"},"nodes":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers in the grid"},"autoCreated":{"type":"boolean","description":"Created by the platform rather than the user"},"priorityMode":{"type":"string","enum":["absolute","weighted"],"description":"How app priorities apply when the grid is contended"},"needsReconciliation":{"type":"boolean","description":"Membership changes are still being applied"},"volumes":{"type":"array","items":{"type":"object","properties":{"mountPath":{"type":"string"},"maxSize":{"type":"number"},"tag":{"type":"string"}},"required":["mountPath","maxSize","tag"],"additionalProperties":false}},"networks":{"type":"array","items":{"type":"object","properties":{"cidr":{"type":"string"},"tag":{"type":"string"}},"required":["cidr","tag"],"additionalProperties":false}},"warning":{"description":"Set when the grid was created but a follow-up step failed","type":["string","null"]}},"required":["id","name","org","access","visibility","createdBy","nodes","autoCreated","priorityMode","needsReconciliation","volumes","networks","warning"],"additionalProperties":false},"OrgRef":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"}},"required":["slug","name"],"additionalProperties":false,"description":"The organization that owns it"},"Access":{"type":"object","properties":{"role":{"type":"string","enum":["admin","developer","viewer"],"description":"The caller's role on it"},"outside":{"type":"boolean","description":"Shared with the caller from another organization"},"isCreator":{"type":"boolean","description":"The caller created it"}},"required":["role","outside","isCreator"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false}}}}
```

## The Server object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Server":{"type":"object","properties":{"address":{"type":"string","description":"Server (node) address"},"name":{"description":"Display name: the caller's own name for the server when set; else, for a server rented from the marketplace, its hardware and Kinesis region; else the hostname","type":["string","null"]},"hostname":{"description":"Null for servers rented from the marketplace","type":["string","null"]},"csp":{"description":"Cloud provider; null for servers rented from the marketplace","type":["string","null"]},"region":{"description":"Provider region; null for servers rented from the marketplace","type":["string","null"]},"zone":{"description":"Provider zone; null for servers rented from the marketplace","type":["string","null"]},"instanceType":{"description":"Provider instance type; null for servers rented from the marketplace","type":["string","null"]},"kcRegion":{"description":"Kinesis region the server serves, e.g. us-west","type":["string","null"]},"owned":{"type":"boolean","description":"The organization owns this server (false for servers seen through a shared grid)"},"ownerName":{"description":"The organization that owns it; null for rented servers","type":["string","null"]},"addedBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who added it to the organization. Null when nobody is on record, for another organization's server, and for a credential without members:read"},"state":{"type":"string"},"hibernation":{"type":"string","description":"active, hibernated, hibernating or waking"},"lastPulseAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"specs":{"type":"object","properties":{"cpuThreads":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"gpus":{"type":"array","items":{"type":"object","properties":{"model":{"type":"string"},"count":{"type":"number"},"vramBytes":{"type":"number"}},"required":["model","count","vramBytes"],"additionalProperties":false}}},"required":["cpuThreads","ramBytes","storageBytes","gpus"],"additionalProperties":false},"utilization":{"type":"object","properties":{"cpu":{"type":"number"},"ram":{"type":"number"},"storage":{"type":"number"},"gpu":{"type":"number"},"vram":{"type":"number"}},"required":["cpu","ram","storage","gpu","vram"],"additionalProperties":false,"description":"Percentages from the last pulse"},"runningApps":{"type":"integer"},"lease":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"gridId":{"type":"string"},"availableUntil":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"priceCentsPerHour":{"type":"number"}},"required":["id","gridId","availableUntil","priceCentsPerHour"],"additionalProperties":false},{"type":"null"}],"description":"Spot lease, for servers rented from the marketplace"},"provisionStatus":{"description":"Provisioning order status while the server is being created","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["address","name","hostname","csp","region","zone","instanceType","kcRegion","owned","ownerName","addedBy","state","hibernation","lastPulseAt","specs","utilization","runningApps","lease","provisionStatus","createdAt"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false}}}}
```

## The PendingServer object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"PendingServer":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"address":{"description":"Server address once the machine has registered; null while it is still being created","type":["string","null"]},"status":{"type":"string"},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"csp":{"type":["string","null"]},"region":{"type":["string","null"]},"instanceType":{"type":["string","null"]},"gridIds":{"type":"array","items":{"type":"string"},"description":"Grids the server joins once it registers"},"attempts":{"type":"object","properties":{"made":{"type":"integer"},"max":{"type":"integer"}},"required":["made","max"],"additionalProperties":false},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["orderId","address","status","failure","csp","region","instanceType","gridIds","attempts","createdAt"],"additionalProperties":false}}}}
```

## The ProvisionResult object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"ProvisionResult":{"type":"object","properties":{"dryRun":{"type":"boolean"},"csp":{"type":"string"},"region":{"type":"string"},"instanceType":{"type":"string"},"count":{"type":"integer"},"estimate":{"$ref":"#/components/schemas/RunwayEstimate"},"orders":{"type":"array","items":{"$ref":"#/components/schemas/ProvisionOrder"},"description":"Empty for a dry run"}},"required":["dryRun","csp","region","instanceType","count","estimate","orders"],"additionalProperties":false},"RunwayEstimate":{"type":"object","properties":{"instanceCount":{"type":"integer"},"totalHourlyCostCents":{"type":"number","description":"Hourly cost of the whole order"},"totalRequiredCents":{"type":"number","description":"Balance needed to cover the minimum runtime"},"minimumHours":{"type":"number","description":"Hours of runtime the balance must cover before an order is accepted"},"balanceCents":{"type":"number"},"overLimitCents":{"type":"number"},"availableRunwayCents":{"type":"number","description":"Balance plus the over-limit allowance"},"topUpNeededCents":{"type":"number","description":"0 when the balance suffices"}},"required":["instanceCount","totalHourlyCostCents","totalRequiredCents","minimumHours","balanceCents","overLimitCents","availableRunwayCents","topUpNeededCents"],"additionalProperties":false},"ProvisionOrder":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"status":{"type":"string"},"count":{"type":"integer","description":"Servers in the order"},"csp":{"type":["string","null"]},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"dismissed":{"type":"boolean","description":"Hidden from the pending list (servers.orders.dismiss)"}},"required":["orderId","status","count","csp","failure","createdAt","updatedAt","dismissed"],"additionalProperties":false}}}}
```

## The RunwayEstimate object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"RunwayEstimate":{"type":"object","properties":{"instanceCount":{"type":"integer"},"totalHourlyCostCents":{"type":"number","description":"Hourly cost of the whole order"},"totalRequiredCents":{"type":"number","description":"Balance needed to cover the minimum runtime"},"minimumHours":{"type":"number","description":"Hours of runtime the balance must cover before an order is accepted"},"balanceCents":{"type":"number"},"overLimitCents":{"type":"number"},"availableRunwayCents":{"type":"number","description":"Balance plus the over-limit allowance"},"topUpNeededCents":{"type":"number","description":"0 when the balance suffices"}},"required":["instanceCount","totalHourlyCostCents","totalRequiredCents","minimumHours","balanceCents","overLimitCents","availableRunwayCents","topUpNeededCents"],"additionalProperties":false}}}}
```

## The ProvisionOrder object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"ProvisionOrder":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"status":{"type":"string"},"count":{"type":"integer","description":"Servers in the order"},"csp":{"type":["string","null"]},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"dismissed":{"type":"boolean","description":"Hidden from the pending list (servers.orders.dismiss)"}},"required":["orderId","status","count","csp","failure","createdAt","updatedAt","dismissed"],"additionalProperties":false}}}}
```

## The ServerActionResult object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"ServerActionResult":{"type":"object","properties":{"address":{"type":"string"},"ok":{"type":"boolean"},"error":{"type":["string","null"]}},"required":["address","ok","error"],"additionalProperties":false}}}}
```

## The PowerActionResult object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"PowerActionResult":{"type":"object","properties":{"address":{"type":"string"},"ok":{"type":"boolean"},"error":{"type":["string","null"]},"reason":{"anyOf":[{"type":"string","enum":["out_of_stock","not_hibernatable","not_active","unknown"]},{"type":"null"}],"description":"Why the request was refused or failed: out_of_stock means retry later; not_hibernatable and not_active are screened before the backend is asked"}},"required":["address","ok","error","reason"],"additionalProperties":false}}}}
```

## The ProvisionOrderDetail object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"ProvisionOrderDetail":{"type":"object","properties":{"order":{"$ref":"#/components/schemas/ProvisionOrder"},"instances":{"type":"array","items":{"$ref":"#/components/schemas/PendingServer"},"description":"One entry per machine in the order; address is set once the machine has registered"},"done":{"type":"boolean","description":"The order reached a terminal state: complete, failed or cancelled"}},"required":["order","instances","done"],"additionalProperties":false},"ProvisionOrder":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"status":{"type":"string"},"count":{"type":"integer","description":"Servers in the order"},"csp":{"type":["string","null"]},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"dismissed":{"type":"boolean","description":"Hidden from the pending list (servers.orders.dismiss)"}},"required":["orderId","status","count","csp","failure","createdAt","updatedAt","dismissed"],"additionalProperties":false},"PendingServer":{"type":"object","properties":{"orderId":{"type":"string","description":"Provisioning order id"},"address":{"description":"Server address once the machine has registered; null while it is still being created","type":["string","null"]},"status":{"type":"string"},"failure":{"description":"Failure category when the order is failing","type":["string","null"]},"csp":{"type":["string","null"]},"region":{"type":["string","null"]},"instanceType":{"type":["string","null"]},"gridIds":{"type":"array","items":{"type":"string"},"description":"Grids the server joins once it registers"},"attempts":{"type":"object","properties":{"made":{"type":"integer"},"max":{"type":"integer"}},"required":["made","max"],"additionalProperties":false},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["orderId","address","status","failure","csp","region","instanceType","gridIds","attempts","createdAt"],"additionalProperties":false}}}}
```

## The ByodToken object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"ByodToken":{"type":"object","properties":{"token":{"type":"string","description":"Provision token; single use"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"needVpn":{"type":"boolean","description":"The machine joins through Kinesis Remote Reach (VPN-backed)"},"expiresInSec":{"type":"integer"},"installCommand":{"type":"string","description":"Run this on the machine as root; it installs the agent and registers it"}},"required":["token","expiresAt","needVpn","expiresInSec","installCommand"],"additionalProperties":false}}}}
```

## The ServerDetail object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"ServerDetail":{"type":"object","properties":{"address":{"type":"string","description":"Server (node) address"},"name":{"description":"Display name: the caller's own name for the server when set; else, for a server rented from the marketplace, its hardware and Kinesis region; else the hostname","type":["string","null"]},"hostname":{"description":"Null for servers rented from the marketplace","type":["string","null"]},"csp":{"description":"Cloud provider; null for servers rented from the marketplace","type":["string","null"]},"region":{"description":"Provider region; null for servers rented from the marketplace","type":["string","null"]},"zone":{"description":"Provider zone; null for servers rented from the marketplace","type":["string","null"]},"instanceType":{"description":"Provider instance type; null for servers rented from the marketplace","type":["string","null"]},"kcRegion":{"description":"Kinesis region the server serves, e.g. us-west","type":["string","null"]},"owned":{"type":"boolean","description":"The organization owns this server (false for servers seen through a shared grid)"},"ownerName":{"description":"The organization that owns it; null for rented servers","type":["string","null"]},"addedBy":{"anyOf":[{"$ref":"#/components/schemas/Person"},{"type":"null"}],"description":"Who added it to the organization. Null when nobody is on record, for another organization's server, and for a credential without members:read"},"state":{"type":"string"},"hibernation":{"type":"string","description":"active, hibernated, hibernating or waking"},"lastPulseAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"specs":{"type":"object","properties":{"cpuThreads":{"type":"number"},"ramBytes":{"type":"number"},"storageBytes":{"type":"number"},"gpus":{"type":"array","items":{"type":"object","properties":{"model":{"type":"string"},"count":{"type":"number"},"vramBytes":{"type":"number"}},"required":["model","count","vramBytes"],"additionalProperties":false}}},"required":["cpuThreads","ramBytes","storageBytes","gpus"],"additionalProperties":false},"utilization":{"type":"object","properties":{"cpu":{"type":"number"},"ram":{"type":"number"},"storage":{"type":"number"},"gpu":{"type":"number"},"vram":{"type":"number"}},"required":["cpu","ram","storage","gpu","vram"],"additionalProperties":false,"description":"Percentages from the last pulse"},"runningApps":{"type":"integer"},"lease":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"gridId":{"type":"string"},"availableUntil":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"priceCentsPerHour":{"type":"number"}},"required":["id","gridId","availableUntil","priceCentsPerHour"],"additionalProperties":false},{"type":"null"}],"description":"Spot lease, for servers rented from the marketplace"},"provisionStatus":{"description":"Provisioning order status while the server is being created","type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]},"ipAddress":{"description":"Public address; null unless the server belongs to the selected organization","type":["string","null"]},"version":{"description":"Agent version the server runs","type":["string","null"]},"countryCode":{"type":["string","null"]},"autoWakeup":{"type":"boolean","description":"The platform wakes the server when an app needs it"},"gridIds":{"type":"array","items":{"type":"string"},"description":"Grids the server belongs to, among those the caller can see"},"apps":{"type":"array","items":{"type":"object","properties":{"guid":{"type":"string","description":"Runtime identifier of the app (list_apps shows it as guid)"},"image":{"type":["string","null"]},"state":{"type":"string","description":"Container state on this server"},"targetState":{"type":"string","enum":["running","stopped","unknown"]},"reportedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["guid","image","state","targetState","reportedAt"],"additionalProperties":false},"description":"Apps placed on the server and their container state there"},"order":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Provisioning order id"},"status":{"type":"string"},"failure":{"type":["string","null"]},"attempts":{"type":"object","properties":{"made":{"type":"integer"},"max":{"type":"integer"}},"required":["made","max"],"additionalProperties":false},"awaitingStockSince":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"Set while a wake waits for provider stock"}},"required":["id","status","failure","attempts","awaitingStockSince"],"additionalProperties":false},{"type":"null"}],"description":"The provisioning order behind a Kinesis-provisioned server; null for an attached machine"}},"required":["address","name","hostname","csp","region","zone","instanceType","kcRegion","owned","ownerName","addedBy","state","hibernation","lastPulseAt","specs","utilization","runningApps","lease","provisionStatus","createdAt","ipAddress","version","countryCode","autoWakeup","gridIds","apps","order"],"additionalProperties":false},"Person":{"type":"object","properties":{"userId":{"type":"string","description":"User id, as members.list and the sharing calls use it"},"name":{"type":["string","null"]},"email":{"type":"string"}},"required":["userId","name","email"],"additionalProperties":false}}}}
```

## The ServerLogs object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"ServerLogs":{"type":"object","properties":{"logs":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"source":{"type":"string","description":"Component that wrote the line, e.g. the agent, the runtime, GPU setup"},"level":{"type":"string","enum":["trace","info","warning","error","unknown"]},"text":{"type":"string"}},"required":["at","source","level","text"],"additionalProperties":false},"description":"Oldest to newest; the last line is the most recent"},"truncated":{"type":"boolean","description":"The window holds more lines than limit; the oldest were dropped (pass until to read further back)"}},"required":["logs","truncated"],"additionalProperties":false}}}}
```

## The SshKey object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"SshKey":{"type":"object","properties":{"id":{"type":"string","description":"Key id"},"label":{"type":"string"},"fingerprint":{"type":"string"},"servers":{"type":"array","items":{"type":"string"},"description":"Addresses of the servers the key is installed on"},"createdAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}]}},"required":["id","label","fingerprint","servers","createdAt"],"additionalProperties":false}}}}
```

## The Region object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Region":{"type":"object","properties":{"id":{"type":"string","description":"Region id to use when provisioning"},"name":{"type":"string"},"location":{"type":"string","description":"City or area"},"recommended":{"type":"boolean"}},"required":["id","name","location","recommended"],"additionalProperties":false}}}}
```

## The InstanceType object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"InstanceType":{"type":"object","properties":{"id":{"type":"string","description":"Instance type id to use when provisioning"},"name":{"type":"string"},"category":{"type":"string","description":"General Purpose, Compute Optimized, GPU, ..."},"specs":{"type":"object","properties":{"vCpus":{"type":"number"},"memory":{"type":"string","description":"Human-readable, e.g. 8 GiB"},"storage":{"type":"string"},"networkPerformance":{"type":"string"},"gpuType":{"type":"string"},"gpuManufacturer":{"type":"string"},"gpuCount":{"type":"number"},"gpuMemory":{"type":"string"}},"required":["vCpus","memory"],"additionalProperties":false},"pricePerHour":{"type":"number","description":"Customer price in USD per hour"},"inStock":{"description":"null when the provider does not report stock","type":["boolean","null"]}},"required":["id","name","category","specs","pricePerHour","inStock"],"additionalProperties":false}}}}
```

## The MarketplaceBrowse object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"MarketplaceBrowse":{"type":"object","properties":{"listings":{"type":"array","items":{"$ref":"#/components/schemas/MarketplaceListing"}},"summary":{"type":"object","properties":{"availableMachines":{"type":"integer"},"configurations":{"type":"integer"},"fromPriceCentsPerGpuHour":{"type":"number"},"fromPriceCentsPerHour":{"type":"number"}},"required":["availableMachines","configurations"],"additionalProperties":false},"truncated":{"type":"boolean","description":"The scan hit its cap; counts are lower bounds"}},"required":["listings","summary","truncated"],"additionalProperties":false},"MarketplaceListing":{"type":"object","properties":{"configId":{"type":"string","description":"Stable id of this hardware profile and country bucket"},"hardwareProfile":{"type":"string"},"gpuModel":{"description":"null for CPU-only machines","type":["string","null"]},"gpusPerMachine":{"type":"integer"},"vramGbPerGpu":{"type":"number"},"cpuThreads":{"type":"number"},"minCpuPackages":{"description":"Smallest physical CPU count in the bucket","type":"number"},"ramGb":{"type":"number"},"storageGb":{"type":"number"},"countryCode":{"type":"string","description":"ISO country code; empty when unreported"},"locationLabel":{"type":"string"},"tierIds":{"type":"array","items":{"type":"string"}},"isVetted":{"type":"boolean"},"totalMachines":{"type":"integer"},"availableMachines":{"type":"integer"},"minPriceCentsPerHour":{"type":"number","description":"Cheapest machine-hour in the bucket"},"maxPriceCentsPerHour":{"type":"number"},"pricePerGpuHourCents":{"type":"number"},"guaranteedDays":{"description":"Weakest availability guarantee in the bucket","type":"number"},"isOpenEnded":{"type":"boolean","description":"Every machine is available until its provider changes its mind"},"noticeHours":{"type":"number"}},"required":["configId","hardwareProfile","gpuModel","gpusPerMachine","countryCode","locationLabel","tierIds","isVetted","totalMachines","availableMachines","minPriceCentsPerHour","maxPriceCentsPerHour","isOpenEnded","noticeHours"],"additionalProperties":false}}}}
```

## The MarketplaceListing object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"MarketplaceListing":{"type":"object","properties":{"configId":{"type":"string","description":"Stable id of this hardware profile and country bucket"},"hardwareProfile":{"type":"string"},"gpuModel":{"description":"null for CPU-only machines","type":["string","null"]},"gpusPerMachine":{"type":"integer"},"vramGbPerGpu":{"type":"number"},"cpuThreads":{"type":"number"},"minCpuPackages":{"description":"Smallest physical CPU count in the bucket","type":"number"},"ramGb":{"type":"number"},"storageGb":{"type":"number"},"countryCode":{"type":"string","description":"ISO country code; empty when unreported"},"locationLabel":{"type":"string"},"tierIds":{"type":"array","items":{"type":"string"}},"isVetted":{"type":"boolean"},"totalMachines":{"type":"integer"},"availableMachines":{"type":"integer"},"minPriceCentsPerHour":{"type":"number","description":"Cheapest machine-hour in the bucket"},"maxPriceCentsPerHour":{"type":"number"},"pricePerGpuHourCents":{"type":"number"},"guaranteedDays":{"description":"Weakest availability guarantee in the bucket","type":"number"},"isOpenEnded":{"type":"boolean","description":"Every machine is available until its provider changes its mind"},"noticeHours":{"type":"number"}},"required":["configId","hardwareProfile","gpuModel","gpusPerMachine","countryCode","locationLabel","tierIds","isVetted","totalMachines","availableMachines","minPriceCentsPerHour","maxPriceCentsPerHour","isOpenEnded","noticeHours"],"additionalProperties":false}}}}
```

## The Balance object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Balance":{"type":"object","properties":{"balanceCents":{"type":"number","description":"Paid balance"},"promoBalanceCents":{"type":"number","description":"Promotional credit, spent first"},"currency":{"type":"string"},"overLimitCents":{"type":"number","description":"How far below zero the balance may go"},"status":{"type":["string","null"]},"mode":{"type":"string","description":"Billing mode of the organization"},"frozen":{"type":"boolean","description":"The organization is frozen: writes are refused until it is unfrozen"},"autoReload":{"$ref":"#/components/schemas/AutoReload"}},"required":["balanceCents","promoBalanceCents","currency","overLimitCents","status","mode","frozen","autoReload"],"additionalProperties":false},"AutoReload":{"type":"object","properties":{"enabled":{"type":"boolean","description":"The saved card is charged automatically when the balance drops below the threshold"},"thresholdCents":{"description":"Balance that triggers a reload","type":["number","null"]},"targetCents":{"description":"Balance after a reload","type":["number","null"]}},"required":["enabled","thresholdCents","targetCents"],"additionalProperties":false}}}}
```

## The AutoReload object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"AutoReload":{"type":"object","properties":{"enabled":{"type":"boolean","description":"The saved card is charged automatically when the balance drops below the threshold"},"thresholdCents":{"description":"Balance that triggers a reload","type":["number","null"]},"targetCents":{"description":"Balance after a reload","type":["number","null"]}},"required":["enabled","thresholdCents","targetCents"],"additionalProperties":false}}}}
```

## The CostSummary object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"CostSummary":{"type":"object","properties":{"start":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"end":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"currency":{"type":"string"},"totalCost":{"type":"number","description":"Total for the range, in the billing currency"},"byProject":{"type":"array","items":{"type":"object","properties":{"projectId":{"type":"string"},"projectName":{"type":"string"},"cost":{"type":"number"}},"required":["projectId","projectName","cost"],"additionalProperties":false}},"byApp":{"type":"array","items":{"type":"object","properties":{"appId":{"type":"string"},"appName":{"type":"string"},"projectId":{"type":"string"},"cost":{"type":"number"}},"required":["appId","appName","projectId","cost"],"additionalProperties":false}},"byResource":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"cost":{"type":"number"},"usage":{"type":"number"},"unit":{"type":"string"}},"required":["type","cost","usage","unit"],"additionalProperties":false},"description":"Cost per billed resource (CPU, GPU, RAM, storage, bandwidth, ...)"},"daily":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","description":"YYYY-MM-DD"},"cost":{"type":"number"}},"required":["date","cost"],"additionalProperties":false}}},"required":["start","end","currency","totalCost","byProject","byApp","byResource","daily"],"additionalProperties":false}}}}
```

## The Topup object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Topup":{"type":"object","properties":{"id":{"type":"string","description":"Top-up id; also the Stripe payment intent id"},"status":{"type":"string","enum":["succeeded","processing","requires_action","failed"],"description":"Charge state. Credits are added once it is succeeded and the payment has been recorded"},"amountCents":{"type":"integer","description":"Amount charged, in US cents"},"credited":{"type":"boolean","description":"Whether the credits are on the balance yet"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","status","amountCents","credited","createdAt"],"additionalProperties":false}}}}
```

## The CheckoutSession object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"CheckoutSession":{"type":"object","properties":{"id":{"type":"string","description":"Checkout session id"},"url":{"type":"string","description":"Hosted payment page; the user opens it in a browser and pays there"},"amountCents":{"type":"integer","description":"Amount the page will charge, in US cents"},"expiresAt":{"type":"string","format":"date-time","description":"The link stops working after this time"}},"required":["id","url","amountCents","expiresAt"],"additionalProperties":false}}}}
```

## The Invoice object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Invoice":{"type":"object","properties":{"number":{"type":"string","description":"Invoice number; what the other invoice operations take"},"period":{"type":"object","properties":{"start":{"type":"string","description":"YYYY-MM-DD"},"end":{"type":"string","description":"YYYY-MM-DD"}},"required":["start","end"],"additionalProperties":false,"description":"Billing period, inclusive"},"status":{"type":"string","enum":["draft","finalized","paid"]},"partialMonth":{"type":"boolean","description":"The organization's billing began during the period"},"currency":{"type":"string"},"totalCents":{"type":"integer","description":"Usage charged in the period"},"amountDueCents":{"type":"integer","description":"What the organization owes on this invoice; 0 when credit covered it"},"closingBalanceCents":{"type":"integer","description":"Balance at the end of the period; negative means owed"},"createdOn":{"type":"string","description":"YYYY-MM-DD"},"updatedOn":{"anyOf":[{"type":"string","description":"YYYY-MM-DD"},{"type":"null"}]}},"required":["number","period","status","partialMonth","currency","totalCents","amountDueCents","closingBalanceCents","createdOn","updatedOn"],"additionalProperties":false}}}}
```

## The InvoiceDetail object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"InvoiceDetail":{"type":"object","properties":{"number":{"type":"string","description":"Invoice number; what the other invoice operations take"},"period":{"type":"object","properties":{"start":{"type":"string","description":"YYYY-MM-DD"},"end":{"type":"string","description":"YYYY-MM-DD"}},"required":["start","end"],"additionalProperties":false,"description":"Billing period, inclusive"},"status":{"type":"string","enum":["draft","finalized","paid"]},"partialMonth":{"type":"boolean","description":"The organization's billing began during the period"},"currency":{"type":"string"},"totalCents":{"type":"integer","description":"Usage charged in the period"},"amountDueCents":{"type":"integer","description":"What the organization owes on this invoice; 0 when credit covered it"},"closingBalanceCents":{"type":"integer","description":"Balance at the end of the period; negative means owed"},"createdOn":{"type":"string","description":"YYYY-MM-DD"},"updatedOn":{"anyOf":[{"type":"string","description":"YYYY-MM-DD"},{"type":"null"}]},"customer":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":["string","null"]},"billingEmail":{"type":["string","null"]}},"required":["email","name","billingEmail"],"additionalProperties":false},"statement":{"type":"object","properties":{"openingBalanceCents":{"type":"integer"},"closingBalanceCents":{"type":"integer"},"amountDueCents":{"type":"integer"},"paymentsCents":{"type":"integer","description":"Card and off-platform payments received in the period"},"promosCents":{"type":"integer","description":"Promotional credit granted in the period"},"refundsCents":{"type":"integer","description":"Signed"},"adjustmentsCents":{"type":"integer","description":"Signed"},"usageCents":{"type":"integer","description":"Usage charged in the period"},"taxCents":{"type":"integer","description":"Tax due on the amount owed; 0 when nothing is owed or no rate applies"},"taxRateBps":{"type":"integer","description":"Tax rate the invoice was billed under, in basis points"}},"required":["openingBalanceCents","closingBalanceCents","amountDueCents","paymentsCents","promosCents","refundsCents","adjustmentsCents","usageCents","taxCents","taxRateBps"],"additionalProperties":false,"description":"The statement summary: how the balance moved over the period"},"items":{"type":"array","items":{"type":"object","properties":{"resource":{"type":"string","description":"What was billed, e.g. a GPU model, RAM, storage, a server"},"detail":{"type":["string","null"]},"csp":{"type":["string","null"]},"region":{"type":["string","null"]},"zone":{"type":["string","null"]},"kcRegion":{"type":["string","null"]},"quantity":{"type":"number"},"unit":{"type":"string"},"unitPrice":{"type":"number","description":"Price per unit, in currency units (fractional)"},"amount":{"type":"number","description":"quantity times unitPrice, in currency units"}},"required":["resource","detail","csp","region","zone","kcRegion","quantity","unit","unitPrice","amount"],"additionalProperties":false},"description":"Itemized usage"},"credits":{"type":"array","items":{"type":"object","properties":{"ledgerEntryId":{"type":"string"},"type":{"type":"string","description":"Ledger entry type: credit_topup, manual_payment, a promo, refund, adjustment"},"at":{"type":"string","description":"ISO 8601 timestamp"},"amountCents":{"type":"integer","description":"Signed"},"label":{"type":"string"},"paymentMethod":{"anyOf":[{"type":"object","properties":{"brand":{"type":["string","null"]},"last4":{"type":["string","null"]}},"required":["brand","last4"],"additionalProperties":false},{"type":"null"}]}},"required":["ledgerEntryId","type","at","amountCents","label","paymentMethod"],"additionalProperties":false},"description":"Payments, promos, refunds and adjustments within the period"},"finalizedOn":{"anyOf":[{"type":"string","description":"YYYY-MM-DD"},{"type":"null"}]},"paidOn":{"anyOf":[{"type":"string","description":"YYYY-MM-DD"},{"type":"null"}]}},"required":["number","period","status","partialMonth","currency","totalCents","amountDueCents","closingBalanceCents","createdOn","updatedOn","customer","statement","items","credits","finalizedOn","paidOn"],"additionalProperties":false}}}}
```

## The UsageCharge object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"UsageCharge":{"type":"object","properties":{"period":{"type":"string","format":"date-time","description":"Start of the day or hour, in the requested time zone"},"amountCents":{"type":"number","description":"Usage charged in the period, including fractional cents"},"appsCents":{"type":"number","description":"Of amountCents, serverless app usage"},"serversCents":{"type":"number","description":"Of amountCents, dedicated servers"},"spotCents":{"type":"number","description":"Of amountCents, spot leases"},"currency":{"type":"string"}},"required":["period","amountCents","appsCents","serversCents","spotCents","currency"],"additionalProperties":false}}}}
```

## The CreditEntry object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"CreditEntry":{"type":"object","properties":{"id":{"type":"string","description":"Ledger entry id"},"at":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},"type":{"type":"string","enum":["credit_topup","manual_payment","signup_promo","credit_promo","social_media_promo","refund","adjustment"],"description":"credit_topup and manual_payment are money in; the promos are granted credit"},"amountCents":{"type":"integer","description":"Signed: positive adds credit, negative removes it"},"currency":{"type":"string"},"status":{"type":"string","description":"succeeded, pending or failed"},"paymentMethod":{"anyOf":[{"type":"object","properties":{"brand":{"type":["string","null"]},"last4":{"type":["string","null"]}},"required":["brand","last4"],"additionalProperties":false},{"type":"null"}],"description":"The card charged, for card payments"},"note":{"description":"A note left by the person who recorded a manual entry","type":["string","null"]}},"required":["id","at","type","amountCents","currency","status","paymentMethod","note"],"additionalProperties":false}}}}
```

## The PaymentMethod object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"PaymentMethod":{"type":"object","properties":{"id":{"type":"string","description":"Payment method id"},"brand":{"description":"Card brand, e.g. visa","type":["string","null"]},"last4":{"type":["string","null"]},"expMonth":{"anyOf":[{"type":"integer"},{"type":"null"}]},"expYear":{"anyOf":[{"type":"integer"},{"type":"null"}]},"default":{"type":"boolean","description":"The card auto-reload and top-ups charge"},"addedAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"}},"required":["id","brand","last4","expMonth","expYear","default","addedAt"],"additionalProperties":false}}}}
```

## The NodeProtection object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"NodeProtection":{"type":"object","properties":{"enabled":{"type":"boolean","description":"The organization's dedicated servers are hibernated when the balance runs low"},"thresholdHours":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Hours of runway left (balance over spend rate) at which the servers are hibernated; null when off"},"hibernatedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When protection last hibernated the servers"},"deprovisionedAt":{"anyOf":[{"type":"string","format":"date-time","description":"ISO 8601 timestamp (UTC)"},{"type":"null"}],"description":"When a balance past its limit last deprovisioned them"}},"required":["enabled","thresholdHours","hibernatedAt","deprovisionedAt"],"additionalProperties":false}}}}
```

## The Error object

```json
{"openapi":"3.1.0","info":{"title":"Kinesis API","version":"1.5.0"},"components":{"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable error code"},"message":{"type":"string","description":"Human-readable explanation"},"requestId":{"type":"string","description":"Quote this when contacting support"},"issues":{"type":"array","description":"Present on invalid_request: one entry per invalid field","items":{"type":"object","required":["path","message"],"properties":{"path":{"type":"string"},"message":{"type":"string"}}}},"requiredScopes":{"type":"array","items":{"type":"string"},"description":"insufficient_scope: the permissions the key lacks"},"billingUrl":{"type":"string","description":"Dashboard page where the user can resolve a billing problem (insufficient_balance, no_payment_method, ...)"},"topUpNeededCents":{"type":"integer","description":"insufficient_balance: credits to add, in US cents"},"appIds":{"type":"array","items":{"type":"string"},"description":"project_has_running_apps: the apps that are still live"},"resetAt":{"type":"string","format":"date-time","description":"rate_limited, topup_limit: when the window resets"},"retryAfterSec":{"type":"integer","description":"request_in_progress: seconds to wait before retrying"},"limit":{"type":"integer","description":"payload_too_large: bytes allowed; rate_limited: requests per window"},"maxCents":{"type":"integer","description":"amount_too_large: the per-transaction maximum"},"declineCode":{"type":"string","description":"card_declined: the issuer's decline code"},"paymentIntentId":{"type":"string","description":"card outcomes: the Stripe payment intent involved"},"allowed":{"type":"array","items":{"type":"string"},"description":"method_not_allowed: the methods the path accepts"}},"additionalProperties":true}}}}}}
```


# MCP & agents

Deploy, inspect and operate Kinesis from your agent.

Connect a compatible agent to Kinesis over the Model Context Protocol. Use natural-language workflows backed by tools for projects, apps, repositories, grids, servers and billing.

## Choose your app

Select the app you use for its setup and sign-in steps.

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FtCAgucjIpdRrICXIeBbi%2Fclaude-code.png?alt=media" alt="" data-size="line"> Claude Code</h3></td><td>One command, then authenticate in /mcp.</td><td><a href="/mcp/connect/claude-code">Claude Code</a></td></tr><tr><td><h3><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FIKbZexyrwTbFfVHI16GH%2Fcodex-openai.png?alt=media" alt="" data-size="line"> Codex</h3></td><td>CLI setup, browser sign-in or a headless flow.</td><td><a href="/mcp/connect/codex">Codex</a></td></tr><tr><td><h3><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FP0Y3DvIYCtU2m1rT6ec5%2Fcursor.png?alt=media" alt="" data-size="line"> Cursor</h3></td><td>Connect the server or add the deployment skill.</td><td><a href="/mcp/connect/cursor">Cursor</a></td></tr><tr><td><h3><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2F4iFI8d8GMqHLli1WbLnv%2Fcustom-agent.svg?alt=media" alt="" data-size="line"> Custom agent</h3></td><td>Streamable HTTP, OAuth and API-key settings.</td><td><a href="/mcp/connect/custom-agent">Custom agent</a></td></tr></tbody></table>

{% hint style="info" %}
Kinesis directory submissions for Claude, ChatGPT and Cursor are awaiting approval. These manual setups are available now.
{% endhint %}

## Start with a connection check

After connecting, send this prompt in your agent:

```
Use Kinesis to show my identity and list the organizations I can access.
Then ask which organization to use and list its projects. Do not change anything.
```

Then try a [deployment or troubleshooting workflow](/mcp/workflows), or explore the [tool reference](/mcp/tools).

## What your agent can do

* Deploy from an image, a template, a connected GitHub repository or a clone of a public repository; copy environment variables with `envFrom`.
* Read configuration, status, runtime logs, usage and image-build output; check and roll back to an earlier stored build image.
* Check placement, move apps, manage logs, operate grids and dedicated servers, attach machines and manage SSH keys.
* Discover and create organizations; manage members, invitations, settings and the audit log within your role and scopes. Add organization members to private resources and handle notifications.
* Inspect invoices and ledger history, manage saved payment methods and node protection, and redeem vouchers. Adding credits and configuring auto-reload happen in the portal.

Serverless placement is CPU-only; GPU apps need a grid. Logs are raw output and may contain sensitive data.

Actions run as the connected person in each organization, with their role there. Organization-level actions take its slug as `org`. A connection can be limited to chosen organizations; a project-limited key cannot run organization-wide administration. Spending and destructive operations have explicit requirements; the tool reference describes them.

Use `list_orgs` (or `account` → `list_orgs` in compact mode) to choose an organization, then `read_docs` with `topic: "organizations"` for the rules. [Organization actions](/mcp/tools/organizations) and [member actions](/mcp/tools/members) include full schemas. Older OAuth connections need approval for the new organization scopes.

For integrations that require credential restrictions, use the [OpenAI profile](/mcp/openai-profile): `?profile=openai` exposes 94 individual tools and restricts credential inputs and outputs. The ordinary compact connection remains available.

## Connection settings

Use the [shared connection options](/mcp/connect#connection-options) to choose individual or compact tools, load selected groups, or expose read-only tools. The [custom agent guide](/mcp/connect/custom-agent) gives the server URL and generic authentication steps.

[OAuth details for client developers](/mcp/oauth) · [Authentication and scopes](/api/authentication)


# Connect an MCP client

Choose your app, connect to Kinesis, and tune the tools it can discover.

Choose your app for copyable setup instructions. Use OAuth to sign in with your Kinesis account; use an API key for automation that cannot open a browser.

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h3><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FtCAgucjIpdRrICXIeBbi%2Fclaude-code.png?alt=media" alt="" data-size="line"> Claude Code</h3></td><td>One command, then authenticate in /mcp.</td><td><a href="/mcp/connect/claude-code">Claude Code</a></td></tr><tr><td><h3><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FIKbZexyrwTbFfVHI16GH%2Fcodex-openai.png?alt=media" alt="" data-size="line"> Codex</h3></td><td>CLI setup, browser sign-in or a headless flow.</td><td><a href="/mcp/connect/codex">Codex</a></td></tr><tr><td><h3><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FP0Y3DvIYCtU2m1rT6ec5%2Fcursor.png?alt=media" alt="" data-size="line"> Cursor</h3></td><td>Connect the server or add the deployment skill.</td><td><a href="/mcp/connect/cursor">Cursor</a></td></tr><tr><td><h3><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2F4iFI8d8GMqHLli1WbLnv%2Fcustom-agent.svg?alt=media" alt="" data-size="line"> Custom agent</h3></td><td>Streamable HTTP, OAuth and API-key settings.</td><td><a href="/mcp/connect/custom-agent">Custom agent</a></td></tr></tbody></table>

Kinesis directory submissions for Claude, ChatGPT and Cursor are awaiting approval. These manual setups are available now.

## Before you connect

You need a [Kinesis account](https://portal.kinesis.network/signup) and membership in the organization you want to use. On the consent screen, review permissions and choose which organizations the connection may reach. Actions follow your role in each organization.

API keys are created in [Account settings → API keys](https://portal.kinesis.network/dashboard/account). Choose the scopes, expiry and organization or project limits your integration needs. If an organization requires SSO, complete its [SSO sign-in](/organizations/single-sign-on) before approving a connection or creating a key that can reach it.

## Connection options

The same URL options work across clients. Append the first option with `?` and additional options with `&`; quote the full URL in shell commands.

| Option                  | Effect                                         |
| ----------------------- | ---------------------------------------------- |
| `?mode=all`             | Expose one tool per action                     |
| `?readonly=true`        | Expose only read tools                         |
| `?groups=apps,projects` | Load only the named groups, plus built-in docs |

For a focused read-only connection that can also identify you and your organizations:

```
https://mcp.kinesis.network/mcp?mode=all&groups=account,projects,apps&readonly=true
```

The Claude Code and Codex quickstarts use individual actions; Cursor uses the smaller default compact catalog. Both modes are supported. Choose the setup that fits your client, then narrow it if needed. Filters change tool discovery; they do not grant extra scopes or resource access.

## Choose a catalog mode

**Compact is the default.** Without filters, it exposes 18 tools. Reads and changes are separated so clients can distinguish inspection from mutation.

| Resource group | Read tool  | Write tool       |
| -------------- | ---------- | ---------------- |
| Account        | `account`  | `account_write`  |
| Projects       | `projects` | `projects_write` |
| Apps           | `apps`     | `apps_write`     |
| Sharing        | `sharing`  | `sharing_write`  |
| Grids          | `grids`    | `grids_write`    |
| Servers        | `servers`  | `servers_write`  |
| Catalog        | `catalog`  | None             |
| Billing        | `billing`  | `billing_write`  |

These tools run named actions with `{action, params}`. The three remaining tools are `browse_github`, `read_docs` and `describe_action`.

To check your identity, call tool `account` with:

```json
{"action": "whoami", "params": {}}
```

To get an action's complete argument schema, call `describe_action`:

```json
{"action": "create_app"}
```

It returns `action`, `tool`, `title`, `description`, `inputSchema` and `annotations`. For `create_app`, `tool` is `apps_write`; for `get_app_status`, it is `apps`. Use it before constructing complex inputs such as `create_app` or `update_app`. It describes actions in the selected folded groups and is present only when at least one such group is loaded. It does not execute the action.

For a client or integration that calls individual action names, connect with:

```
https://mcp.kinesis.network/mcp?mode=all
```

This exposes 95 tools, including `whoami` and `create_app` directly. Pass their arguments without an `action`/`params` wrapper. `browse_github` and `read_docs` are direct calls in both modes; `describe_action` is a compact-mode helper.

Both modes enforce the same action schemas and permissions. Compact descriptions omit pagination and idempotency fields from short signatures; those arguments remain supported. Some advertised schemas shorten nested configuration, and compact direct-tool schemas omit validation-only constraints. `describe_action` and the [reference](/mcp/tools) provide complete schemas; calls still enforce the full validation rules.

## OpenAI profile

Use `https://mcp.kinesis.network/mcp?profile=openai` for 94 individual action tools with credential restrictions and top-level output schemas. It omits `get_byod_token` and `describe_action`; it cannot be combined with `mode=compact`. Group and read-only filters still apply. Read the [OpenAI profile guide](/mcp/openai-profile) before using its inputs.

## Load selected groups

```
https://mcp.kinesis.network/mcp?groups=account,projects,apps
```

For inspection without write actions:

```
https://mcp.kinesis.network/mcp?groups=account,projects,apps&readonly=true
```

| Group      | Includes                                                                                        |
| ---------- | ----------------------------------------------------------------------------------------------- |
| `account`  | Identity, organizations, settings, audit, received organization invitations and notifications   |
| `projects` | Project listing, creation, name/visibility updates and deletion                                 |
| `apps`     | Apps, templates, placement, moves, lifecycle, logs, usage, builds, rollback and domains         |
| `github`   | Connected accounts and repository browsing, including file contents                             |
| `sharing`  | Members, roles, ownership, organization invitations, grantees and existing resource invitations |
| `grids`    | Grid composition, server priority and grid app controls                                         |
| `servers`  | Orders, server details/logs, BYOD tokens, power, Smart Lifecycle and SSH keys                   |
| `catalog`  | Provider catalog and marketplace                                                                |
| `billing`  | Balance, costs, invoices, ledger, payment methods, vouchers and node protection                 |
| `docs`     | Built-in guides; always included                                                                |

No filter loads all groups. `groups` accepts comma-separated values and repeated query parameters. `readonly=true` or `readonly=1` removes write actions before groups are folded. An unknown group or mode returns `400 invalid_request`.

Filters control discovery; scopes and resource access are enforced independently. `account` must be included explicitly when using a group filter that needs identity or notifications.

## Choose the organization

Call `account` with `{"action":"list_orgs","params":{}}`. For organization-level actions, put the returned slug inside `params.org`, for example `{"action":"list_members","params":{"org":"acme"}}` on `sharing`. Resource-ID actions resolve the organization themselves. `list_projects` and `list_apps` span organizations unless `org` filters them.

A role, credential scope and organization/project restriction must all permit the action. `org_required` lists slugs when a call needs one. Connections approved before `members:read`, `members:write`, `org:write` and `audit:read` existed need re-approval for those permissions.

## Verify the connection

1. In compact mode call `account` with `{"action":"whoami"}`. In all mode call `whoami` with `{}`.
2. Call `read_docs` with `{"topic":"getting_started"}`.
3. With `projects:read`, call `projects` with `{"action":"list_projects","params":{}}`, or `list_projects` in all mode.

If organization SSO blocks key creation or OAuth approval in the browser, complete [SSO](/organizations/single-sign-on) first. Existing keys and connections continue working under their permissions.

If an action fails with `insufficient_scope`, inspect `requiredScopes`. Changing the catalog mode or group filter does not grant permission.

## Built-in resources

The server advertises nine Markdown resources. Clients without a resource UI can call `read_docs` with the final path segment as `topic`.

| Resource                         | Topic                                                    |
| -------------------------------- | -------------------------------------------------------- |
| `kinesis://docs/getting_started` | First deployment                                         |
| `kinesis://docs/deploy`          | Sources and app configuration                            |
| `kinesis://docs/operate`         | Lifecycle, logs, builds, rollback and domains            |
| `kinesis://docs/troubleshoot`    | Status and failure diagnosis                             |
| `kinesis://docs/servers`         | Grids, dedicated servers and orders                      |
| `kinesis://docs/billing`         | Credits, costs and payments                              |
| `kinesis://docs/organizations`   | Organizations, members, roles, settings and audit        |
| `kinesis://docs/sharing`         | Adding organization members to resources and grid access |
| `kinesis://docs/errors`          | Error codes and recovery                                 |

The server is stateless and does not provide change-event subscriptions. Send one JSON-RPC message per HTTP request; batches are not supported.

## Troubleshooting

| Symptom                                            | Check                                                                                                   |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `401` before tools load                            | Bearer header, key expiry/revocation or OAuth authorization                                             |
| An individual name such as `create_app` is missing | Default compact mode uses `apps_write` with `action: "create_app"`; use `mode=all` for individual names |
| Missing write actions                              | Check the `_write` tool and the `readonly` filter                                                       |
| An action was sent to the wrong tool               | Follow the error’s named tool, or read `describe_action(...).tool`                                      |
| `add_credits` or `set_auto_reload` is missing      | These actions are not available through MCP; billing belongs to organization admins                     |
| Missing resource family or identity                | `groups` filter; include `account` for `whoami`                                                         |
| `403 insufficient_scope`                           | `requiredScopes` and the user's resource role                                                           |
| `invalid_request` on a compact call                | Read `issues` under `params.*`; action-schema failures also include `inputSchema`                       |
| `404`                                              | Use the MCP host and `/mcp` path above                                                                  |
| A subscription request is refused                  | Use explicit reads with this stateless server                                                           |
| `429 rate_limited`                                 | Honor `Retry-After` and reset headers                                                                   |

Next: [Agent workflows](/mcp/workflows) and [Tool reference](/mcp/tools).


# Claude Code

Connect Claude Code to Kinesis with a command, then sign in from /mcp.

Use Claude Code to inspect projects, deploy apps and operate Kinesis from your terminal. You need Claude Code installed and a [Kinesis account](https://portal.kinesis.network/signup).

## Connect with your Kinesis account

{% stepper %}
{% step %}

### Add the server

Run this in your terminal:

```bash
claude mcp add --transport http --scope user kinesis "https://mcp.kinesis.network/mcp?mode=all"
```

`--scope user` makes Kinesis available in every project. Without it, Claude Code saves the connection for the current directory. The URL is quoted so shells handle its query string correctly.
{% endstep %}

{% step %}

### Sign in

Start Claude Code and run `/mcp`. Select **kinesis**, then **Authenticate**. In the browser, sign in to Kinesis, review the permissions and choose the organizations the connection can reach. Approve, then return to Claude Code.

If an organization requires SSO, complete its SSO sign-in when prompted.
{% endstep %}

{% step %}

### Check the connection

Confirm Kinesis is connected in `/mcp`, then send:

```
Use Kinesis to show my identity and list the organizations I can access.
Then ask which organization to use and list its projects. Do not change anything.
```

This setup exposes individual actions such as `whoami` and `list_orgs`. A successful check returns your Kinesis identity and the organizations available to the connection.
{% endstep %}
{% endstepper %}

## API key for CI or non-interactive use

For CI or `claude -p` without an interactive browser, create a scoped key in [Account settings → API keys](https://portal.kinesis.network/dashboard/account). Set `KINESIS_API_KEY` through your environment or CI secret store, then use the command for your shell:

{% tabs %}
{% tab title="macOS / Linux / WSL" %}

```bash
claude mcp add --transport http --scope user kinesis "https://mcp.kinesis.network/mcp" --header "Authorization: Bearer $KINESIS_API_KEY"
```

{% endtab %}

{% tab title="PowerShell" %}

```powershell
claude mcp add --transport http --scope user kinesis "https://mcp.kinesis.network/mcp" --header "Authorization: Bearer $env:KINESIS_API_KEY"
```

{% endtab %}
{% endtabs %}

Keep `--header` **after the URL**. It accepts multiple values and can consume the server name and URL if placed before them.

{% hint style="warning" %}
Your shell expands the variable before Claude Code saves the configuration. **The API key is stored as plain text in `~/.claude.json`** for this user-scoped setup. Protect that file and exclude it from source control and shared artifacts. OAuth avoids putting an API key in this configuration.
{% endhint %}

The API-key example uses the default compact catalog. See [connection options](/mcp/connect#choose-a-catalog-mode) for individual actions, selected groups and read-only discovery. If `kinesis` already exists, update that entry rather than adding a second connection.

## If it does not connect

Run `claude mcp get kinesis` to inspect the saved server. Use `/mcp` to authenticate or reconnect after a change. For missing permissions, return to Kinesis authorization or update the key's scopes; changing the URL does not grant access.

[Shared troubleshooting](/mcp/connect#troubleshooting) · [Try a workflow](/mcp/workflows) · [Claude Code MCP documentation](https://code.claude.com/docs/en/mcp)


# Codex

Add Kinesis to Codex, sign in with OAuth, or read an API key from your environment.

Connect Kinesis from the Codex CLI. You need Codex installed and a [Kinesis account](https://portal.kinesis.network/signup).

## Connect with your Kinesis account

{% stepper %}
{% step %}

### Add the server

```bash
codex mcp add kinesis --url "https://mcp.kinesis.network/mcp?mode=all"
```

This setup uses individual action names. Codex saves the connection in its user configuration, shared by local Codex clients using the same configuration home.
{% endstep %}

{% step %}

### Sign in

Follow the sign-in page if Codex opens it. Otherwise run:

```bash
codex mcp login kinesis
```

Sign in to Kinesis, review the permissions and select the organizations the connection can reach. Complete organization SSO if prompted, approve, then return to Codex.

On a machine without a browser, use:

```bash
codex mcp login kinesis --no-browser
```

Open the printed authorization URL in a browser on another machine. After approval, paste the resulting callback URL into the waiting terminal when Codex asks for it. Keep that URL private.
{% endstep %}

{% step %}

### Check the connection

Run `codex mcp list`, then start a new Codex session. In the CLI, `/mcp` shows active servers. Send:

```
Use Kinesis to show my identity and list the organizations I can access.
Then ask which organization to use and list its projects. Do not change anything.
```

{% endstep %}
{% endstepper %}

## API key from an environment variable

Create a scoped key in [Account settings → API keys](https://portal.kinesis.network/dashboard/account), and make `KINESIS_API_KEY` available to the process that launches Codex. For CI, use its secret store.

```bash
codex mcp add kinesis --url "https://mcp.kinesis.network/mcp" --bearer-token-env-var KINESIS_API_KEY
```

Codex reads the variable at runtime. This command saves **the variable's name, not the key**, in its configuration. No OAuth login is needed for this option. The example uses the default compact catalog.

## Configure the file directly

Instead of the CLI command, add this to `~/.codex/config.toml`:

```toml
[mcp_servers.kinesis]
url = "https://mcp.kinesis.network/mcp"
# Uncomment for API-key authentication instead of OAuth:
# bearer_token_env_var = "KINESIS_API_KEY"
```

For OAuth, leave the bearer setting commented and run `codex mcp login kinesis`. If the entry already exists, edit it rather than creating a duplicate table. Restart the client or start a new session after configuration changes.

These manual Codex setups use the standard Kinesis endpoint. The [OpenAI MCP profile](/mcp/openai-profile) is a separate option for integrations that require its credential restrictions.

[Connection options](/mcp/connect#choose-a-catalog-mode) · [Troubleshooting](/mcp/connect#troubleshooting) · [Official MCP documentation](https://developers.openai.com/codex/mcp)


# Cursor

Connect Cursor to Kinesis manually, or add the local plugin with its deployment skill.

Connect Cursor's agent to Kinesis with a remote MCP server. For a guided repository deployment workflow, you can also install the Kinesis plugin locally while Marketplace approval is pending.

## Connect the server

{% stepper %}
{% step %}

### Add Kinesis

[**Add Kinesis to Cursor →**](https://cursor.com/en-US/install-mcp?name=kinesis\&config=eyJ1cmwiOiJodHRwczovL21jcC5raW5lc2lzLm5ldHdvcmsvbWNwIn0%3D)

Open the link on a computer with Cursor installed and review its install prompt. If the browser cannot open Cursor, use the file setup below.

Add this entry to **`~/.cursor/mcp.json`** for all projects, or **`.cursor/mcp.json`** in a project for just that project. On Windows, `~` means your user folder, typically `C:\Users\<you>`.

```json
{
  "mcpServers": {
    "kinesis": {
      "url": "https://mcp.kinesis.network/mcp"
    }
  }
}
```

If the file already contains servers, add `kinesis` inside the existing `mcpServers` object; keep the other entries. The URL loads the compact catalog, with separate read and write tools.
{% endstep %}

{% step %}

### Sign in

Open Cursor's **Customize** page and find **kinesis** in MCP servers. Enable it if needed and follow the connection's sign-in prompt. Sign in to Kinesis in the browser, review permissions and choose organizations, then approve and return to Cursor.

Complete organization SSO if prompted. If the new server is missing, run **Developer: Reload Window** from Cursor's command palette.
{% endstep %}

{% step %}

### Check the connection

Start an Agent chat and send:

```
Use Kinesis to show my identity and list the organizations I can access.
Then ask which organization to use and list its projects. Do not change anything.
```

In compact mode, Cursor calls the `account` and `projects` tools with an action and its parameters. You can use natural language; you do not need to write the tool arguments yourself.
{% endstep %}
{% endstepper %}

## Add the deployment skill

The public [Kinesis Cursor plugin](https://github.com/kinesis-network/cursor-plugin) includes the MCP connection and **deploy-to-kinesis** skill. The skill checks your repository, Dockerfile, port and environment, follows the build and returns the deployed address.

While Marketplace approval is pending, install it as a local plugin:

{% tabs %}
{% tab title="macOS / Linux / WSL" %}

```bash
git clone https://github.com/kinesis-network/cursor-plugin ~/.cursor/plugins/local/kinesis-network
```

{% endtab %}

{% tab title="PowerShell" %}

```powershell
git clone https://github.com/kinesis-network/cursor-plugin "$env:USERPROFILE/.cursor/plugins/local/kinesis-network"
```

{% endtab %}
{% endtabs %}

Run **Developer: Reload Window**, then open **Customize** and confirm the Kinesis plugin, MCP server and deployment skill are present. Authenticate its connection when prompted. If you already added the standalone server above, keep one Kinesis connection enabled to avoid duplicate tools.

Local plugin imports must be allowed by your organization. If policy blocks them, ask your Cursor administrator; the standalone MCP setup remains an option when permitted.

To use the skill, ask: **“Use deploy-to-kinesis to deploy this repository to Kinesis.”** Deployments use the selected organization's credits and your Kinesis permissions.

For later updates, open a terminal in that plugin folder and run `git pull --ff-only`, then reload Cursor. Do not clone over an existing folder.

[Connection options](/mcp/connect#choose-a-catalog-mode) · [Troubleshooting](/mcp/connect#troubleshooting) · [Cursor MCP documentation](https://cursor.com/docs/mcp) · [Local plugin documentation](https://cursor.com/docs/plugins#test-plugins-locally)


# Custom agent

Connect any compatible agent or MCP client using Streamable HTTP, OAuth or a scoped API key.

Use these settings for your own agent or any client that supports remote MCP over **Streamable HTTP**. Kinesis hosts the server; you do not need to run a local server process.

| Setting        | Value                                             |
| -------------- | ------------------------------------------------- |
| Server name    | `kinesis`                                         |
| Server URL     | `https://mcp.kinesis.network/mcp`                 |
| Transport      | Streamable HTTP                                   |
| Authentication | OAuth or an API key in the `Authorization` header |

## Connect and authenticate

{% tabs %}
{% tab title="OAuth" %}

1. Add the URL above as a remote HTTP server in your client's MCP settings.
2. Start its sign-in or authorization flow. The client discovers Kinesis authorization metadata from the server's challenge.
3. Sign in to Kinesis, review permissions and select the organizations the connection may reach. Complete organization SSO if prompted.
4. Approve, return to your client and refresh its tool list.

For your own client implementation, follow the [OAuth guide](/mcp/oauth) for discovery, registration, PKCE and refresh-token handling.
{% endtab %}

{% tab title="API key" %}

1. Create a key in [Account settings → API keys](https://portal.kinesis.network/dashboard/account).
2. Choose scopes, organization or project restrictions, and expiry for your integration.
3. Supply the key through your client's secret mechanism or environment, and send this header on MCP requests:

```http
Authorization: Bearer kn_your_api_key
```

`kn_your_api_key` is a placeholder. Do not put the key in the URL, a prompt or a committed configuration file. Your agent needs `projects:read` to list projects; add other [scopes](/api/authentication) for its tasks.
{% endtab %}
{% endtabs %}

## Verify with read-only calls

With the default compact catalog, call tool **`account`**:

```json
{"action":"whoami","params":{}}
```

Then call **`account`** with:

```json
{"action":"list_orgs","params":{}}
```

Choose a returned organization slug and call **`projects`**:

```json
{"action":"list_projects","params":{"org":"acme"}}
```

Replace `acme` with that slug. For individual action names, see [catalog modes](/mcp/connect#choose-a-catalog-mode). Load `account` and `projects` if you apply a group filter and want to run these checks.

## Build a useful agent

Call `read_docs` with `{"topic":"getting_started"}` to learn the platform. Use `describe_action` to obtain a folded action's complete input schema before constructing complex requests. Successful calls return JSON in text content and `structuredContent`; failures include `isError` and a structured error.

Have the user choose the organization before organization-level work. Respect credential scopes, organization roles and each action's confirmation or spending requirements. Follow [agent workflows](/mcp/workflows) for polling, retries and idempotency.

The server is stateless. Use one JSON-RPC message per HTTP request; batches and change-event subscriptions are not supported. A client that accepts only local stdio processes needs remote HTTP support to use this endpoint directly.

[Shared connection options](/mcp/connect#connection-options) · [OpenAI profile](/mcp/openai-profile) · [Tool reference](/mcp/tools)


# OpenAI MCP profile

Use individual MCP actions with the OpenAI profile's credential restrictions and output schemas.

Kinesis provides an MCP profile for OpenAI integrations:

```
https://mcp.kinesis.network/mcp?profile=openai
```

It exposes **94 individual action tools** before group or read-only filtering. The normal catalog has 95 actions; this profile omits `get_byod_token`, which returns a machine provisioning credential. It uses the same authentication, scopes, organization roles and access restrictions as other MCP connections.

## How it differs

| Behavior                                             | Default compact connection                      | `profile=openai`                                            |
| ---------------------------------------------------- | ----------------------------------------------- | ----------------------------------------------------------- |
| Calls                                                | Group tool with `{action, params}`              | Action name with its arguments directly                     |
| Catalog                                              | 18 tools covering 95 actions                    | 94 individual tools                                         |
| `describe_action`                                    | Available for loaded folded groups              | Not present; inspect the action's advertised schema         |
| Result schemas                                       | Full response schemas in this reference         | Advertised `outputSchema` describes top-level result fields |
| Machine provisioning token                           | `get_byod_token` available with its permissions | Omitted; attach your own machine through the portal         |
| Private registry login and secret environment values | Supported where the action schema allows        | Configure in the portal                                     |

Leave `mode` out of this URL, or use `mode=all`. `mode=compact` with this profile is rejected as `400 invalid_request`. Group selection and `readonly=true` still work, for example:

```
https://mcp.kinesis.network/mcp?profile=openai&groups=account,projects,apps&readonly=true
```

`read_docs` remains included. Include `account` when you need `whoami` or `list_orgs`.

## Call actions directly

Call `list_projects` with:

```json
{"org":"acme","limit":50}
```

Call `create_app` directly with its input fields, not an `action` / `params` wrapper. Organization-level calls take `org`; project, app and grid calls resolve the organization from the resource. Read and write annotations remain attached to each individual action.

## Handle credentials in the portal

This profile removes `registryAuth` and environment-variable `secret` fields from advertised input schemas. Runtime checks refuse a supplied private registry login and nonempty environment values that are explicitly secret, already stored as secret, or detected as credentials with high confidence. These refusals are validation errors; switching tool names does not bypass the rule.

Set those values in the Kinesis dashboard. An agent should explain where to set them instead of asking the user to paste credentials into the conversation. Ordinary non-secret configuration can still be supplied through tools.

When reading app environment variables, secret values are null as usual. This profile also returns null for values that look like credentials even if they were not marked secret. Send null back on update to preserve the stored value. A new variable still needs a value; null cannot create one.

`envFrom` can copy an accessible app's environment on the server without returning its secrets. Credential restrictions also apply to values you supply as overrides. Preserve the existing access and scope requirements.

## Logs remain raw output

The credential handling above is not a general log-redaction service. App logs and log exports contain what the app printed; build logs contain what its build steps printed; server logs contain what the machine wrote. Those outputs may contain sensitive data. Request only the logs needed for the task and avoid repeating secrets in responses.

Successful calls return JSON in text content and `structuredContent`. An advertised output schema describes top-level fields, not every nested field; the reference pages provide complete service response schemas. Error handling, safe retries and explicit `confirm: true` arguments follow the normal [agent workflows](/mcp/workflows).

This connection profile describes the API contract; it does not imply a plugin-directory listing or approval status.

[Connect an MCP client](/mcp/connect) · [Tool reference](/mcp/tools)


# OAuth for MCP clients

Authorization discovery, PKCE, scopes and token handling for MCP client implementers.

Kinesis supports browser-based OAuth authorization for the MCP server. API keys remain available for clients that use a static bearer header.

## Discover the server

An unauthenticated MCP request returns `401` with a `WWW-Authenticate` challenge containing `resource_metadata`. Follow that URL rather than hard-coding deployment-specific authorization endpoints.

For the public service:

| Metadata                | URL                                                                     |
| ----------------------- | ----------------------------------------------------------------------- |
| Protected resource      | `https://mcp.kinesis.network/.well-known/oauth-protected-resource/mcp`  |
| Authorization server    | `https://portal.kinesis.network/.well-known/oauth-authorization-server` |
| MCP resource identifier | `https://mcp.kinesis.network/mcp`                                       |

The protected-resource document lists the authorization server, resource identifier, supported scopes and bearer-header authentication. The authorization-server document lists the endpoints and client capabilities.

## Authorization flow

```mermaid
sequenceDiagram
  participant C as MCP client
  participant M as Kinesis MCP
  participant P as Kinesis portal
  participant U as User
  C->>M: Request without token
  M-->>C: 401 + resource metadata
  C->>P: Discover authorization server
  C->>U: Open authorization URL with S256 PKCE
  U->>P: Sign in, approve scopes and organization access
  P-->>C: Authorization code
  C->>P: Exchange code with verifier
  P-->>C: Access token
  C->>M: Authorization: Bearer access token
  M-->>C: MCP response
```

The supported grant types are `authorization_code` and `refresh_token`; the response type is `code`. PKCE uses `S256`. Public clients use token endpoint authentication method `none`. The server advertises dynamic client registration and client ID metadata document support; use the discovery response to select the approach your client implements.

The public endpoint paths are `/oauth/authorize`, `/oauth/token`, `/oauth/register` and `/oauth/revoke` on the portal origin.

## Scopes and token lifetime

OAuth requests use explicit API scope names, such as `apps:read`, `monitoring:read` and `compute:spend`. OAuth does not issue the `*` full-access wildcard. Request `offline_access` when your client needs a refresh token.

The current implementation uses ten-minute authorization codes, one-hour access tokens and thirty-day refresh tokens. Use returned expiry values as the authority. Refresh tokens rotate; replace the stored token after refresh and serialize concurrent refresh attempts.

A refresh request may repeat `offline_access`. Kinesis removes that OAuth-only scope before checking resource permissions. Omitting `scope`, or supplying only `offline_access`, retains the issued scopes. A nonempty resource-scope list may narrow the grant but cannot add permissions; ungranted scopes return `invalid_scope`.

{% hint style="info" %}
OAuth tokens are scoped to the MCP resource. The REST API does not accept them. Use an API key for REST integrations.
{% endhint %}

## Organization access

A connection acts as its person with their role in each organization. Consent can allow all organizations, including ones joined later, or selected organizations. `whoami.limitedToOrgs` describes the limit and `list_orgs` lists reachable organizations. Organization-level actions use the slug as `org`; project, app and grid actions resolve it from their resource.

Losing membership ends that connection’s access in the organization. A connection limited to chosen organizations cannot create another organization or answer received organization invitations.

An organization may require SSO for browser access. Approving a connection that will reach it requires an allowed browser session; complete its SSO and repeat approval if `sso_required` is returned. Existing connections continue working without renewing browser SSO every 24 hours, until access is otherwise ended. This applies to either the default MCP URL or the [OpenAI profile](/mcp/openai-profile).

## Insufficient scope

Kinesis can return an HTTP `403` challenge before a call runs when it can determine that the OAuth connection lacks required scopes. When the scope depends on tool arguments, a tool failure can include an authorization challenge in `_meta["mcp/www_authenticate"]`.

The Organizations release adds `members:read`, `members:write`, `org:write` and `audit:read`. Existing connections do not gain them through token refresh; the user must approve them again. `audit:read` is required separately for the audit log and is not implied by `org:write`.

Read `structuredContent.error.requiredScopes`, request the appropriate authorization step-up and retry only after the new grant is available. The original user's role on the resource still limits what the connection can do.


# Agent workflows

Deploy, diagnose, share and operate Kinesis through MCP actions.

The names below are **actions**. In default compact mode, send reads to the group tool and changes to its `_write` tool with `{action, params}`. With `?mode=all`, call the action name directly. `browse_github` and `read_docs` are direct in both modes.

For `profile=openai`, call individual action names directly. Use the [profile guide](/mcp/openai-profile) for credential restrictions; `get_byod_token` is unavailable and secrets belong in the portal. Logs remain raw output.

Check identity using `account` → `whoami`, then `account` → `list_orgs`. Choose the organization and check your role. Organization-level actions take its slug in `params.org`; project, app and grid calls resolve it from the resource. In compact mode, use `describe_action` before constructing complex arguments.

## Deploy an app

1. Use `projects` → `list_projects`, or `projects_write` → `create_project` with `org`, `name` and optional `visibility: "private"` or `"org"`.
2. Choose an image, an app template using `apps` → `list_templates`, or a GitHub source through `browse_github`. Read template notes for placeholders.
3. For complex hardware or grid requirements, use `apps` → `check_placement` with `projectId` and placement-related `config`. This checks fit without creating an app.
4. Call tool `apps_write` with:

```json
{
  "action": "create_app",
  "params": {
    "projectId": "<project-id>",
    "name": "web",
    "idempotencyKey": "deploy-web-001",
    "config": {
      "image": "nginx:stable",
      "ports": [{"port": 80, "protocol": "http80_load_balancer"}],
      "instances": {"min": 1, "max": 1}
    }
  }
}
```

In all mode, call `create_app` with only the `params` object above.

Creation attempts to start the app and requires `apps:write` and `compute:spend`. The organization pays. Developers need serverless enabled for this example, or must add an accessible grid to `config.grids.ids`. Inspect `start.state`: for `building`, poll `get_build` with the returned `buildId`, then `get_app_status`. Otherwise follow status directly. Use the returned `endpoints` and verify readiness before reporting a running service.

GPU apps require an accessible grid; serverless GPUs are currently paused. A GPU template’s `notes` explains the required `config.grids.ids`.

### Build from a public repository

As a member of the project’s organization with permission to edit it, set `source.kind: "github"` and the connected `installationId`. Choose either `source.repo` or `source.clone`. A clone needs `sourceRepo` and `targetRepoName`; `targetVisibility` defaults to `private`. It creates a repository in the connected account and builds from that copy. Follow the build's `vcs_import` step and subsequent build status.

Use `browse_github` with `kind: "contents"` to inspect a directory or text file. If a repository lacks a Dockerfile, inspect its build instructions and provide `source.dockerfileContent`. Omit `config.image` and `config.registryAuth` for repository builds.

### Reuse an app's environment

Set `envFrom: {projectId, appId, overrides?}` on `create_app` to copy environment variables and their secret values on the server. The caller needs Developer access to the source app. Omit `config.env`; supply per-key changes in `envFrom.overrides`. A null override retains the copied value, and a copied secret remains secret. This is a copy made at creation, not a live link. See [App configuration](/api/app-configuration).

## Diagnose and maintain an app

Start with `apps` → `get_app_status`. Inspect aggregate and per-node state, errors and runtime `workOrders`; grid-backed apps also expose scheduling context such as fill and queue state.

| Finding                             | Next action                                                               |
| ----------------------------------- | ------------------------------------------------------------------------- |
| Building or build failed            | `get_build` and `get_build_logs`                                          |
| Provisioning or stalled placement   | `check_placement`, then `servers` → `get_server` or `get_provision_order` |
| Node health or installation problem | `servers` → `get_server_logs`, for a server in the selected organization  |
| Container errors or repeated exits  | `get_app_logs`; check command, environment, ports and resources           |
| Running but unreachable             | Verify endpoint, listen port and binding to `0.0.0.0`                     |
| High resource use                   | `get_app_usage`                                                           |
| Need a text log export              | `export_logs`; inspect `truncated` before treating the export as complete |

Log retention uses `get_log_retention` and `set_log_retention`: set 1–180 days, or 0 for the platform default. `clear_logs` deletes records older than `olderThan` and requires `confirm: true`.

Read configuration before `update_app`: arrays replace existing arrays, while null retains an existing secret. `move_app` moves an app to another project of the same organization and requires `apps:admin`, an admin role or app-creator permission, and permission to edit the destination; use its returned project ID for subsequent calls.

## Roll back a repository build

1. Use `list_builds` to choose a build whose image was built.
2. Call `apps` → `check_rollback_build` with `projectId`, `appId` and `buildId`. If `imageAvailable` is false, build its commit again with `trigger_build`.
3. With `apps:write` and permission to edit the app, call `apps_write` → `rollback_build`. A running app redeploys; a stopped app uses the image at its next start.
4. Inspect the returned `build` and `redeploying`, then app status.

Rollback changes the image in the current configuration; it does not restore old environment variables or other settings. It cannot run during an active build or before an app has deployed. A later build or automatic deployment replaces it again. The action has write/destructive/idempotent/open-world annotations and no `confirm` argument.

## Invite people and share a project

1. Call `sharing` → `list_members` with `org` to find members and their user IDs.
2. Add a member with `sharing_write` → `add_grantees`: `resourceType`, `resourceId`, and `people: [{userId, role}]`. Access begins immediately. Resource-grant value `editor` means Developer in the portal; `viewer` reads. Grants cannot exceed the organization role, and grids accept `viewer` for use only.
3. For someone new, use `sharing_write` → `invite_member` with `org`, `email`, organization `role` (`admin`, `developer`, `viewer`) and optional `addTo`. The link expires in seven days. Developers invite at their own role or below when enabled.
4. Use `list_member_invitations` and `revoke_member_invitation` for pending invitations. Repeating an invite sends a fresh link; wait at least a minute between sends.

Member reads require `members:read`; changes require `members:write`. Resource sharing and invitation `addTo` also require the resource-family admin scope. Only admins or a project/app’s creator manage its access; only admins manage grid sharing.

Recipients use `account` → `list_org_invitations`, then `account_write` → `answer_org_invitation` with `action: "accept"` or `"decline"`. Credentials limited to organizations or projects cannot answer these invitations. Older resource-invitation actions handle existing invitations only; `invite_user` has been replaced by organization invitations and direct member grants.

Use `set_member_role` to change a role. Use `remove_member` with `confirm: true` to remove someone or leave with your own user ID. Their resources remain and apps keep running. The owner must first use `transfer_ownership` with `confirm: true`; only the current owner may do so.

Manage grants with `list_grantees`, `set_grantee_role` and `revoke_access`. Give up your own grant with `leave_shared_resource`. A remaining project grant or organization-wide visibility may still provide access.

Use `account` → `list_notifications`, and `account_write` for `mark_notifications_seen` or `dismiss_notifications`. Omitting `id` from either write applies to all active notifications.

## Inspect settings and trace activity

Call `account` → `get_org` with `org`. Admins use `account_write` → `update_org` to rename it or change `serverlessEnabled` and `membersCanInvite` with `org:write`. Its slug stays fixed.

Admins read `account` → `list_audit_log` with `org`, optional `limit` and `cursor`, following `nextCursor`. It needs `audit:read` specifically. Entries are latest-recorded-first and include actor, action, outcome, error code, details and changes. Retention is one year. Older OAuth connections need re-approval for new scopes.

## Operate dedicated compute

Work as an organization admin. Include `org` on server, order, SSH and provisioning actions.

1. Use `catalog` → `browse_catalog` to find regions and instance types.
2. Use `servers_write` → `provision_servers` with `dryRun: true` to inspect estimates.
3. When the order matches the user's request, use `dryRun: false` and a new idempotency key.
4. Poll `get_provision_order`, following `pollAfterSec`.
5. Verify `list_servers` or `get_server`. Use `gridIds` during provisioning or add machines to a grid afterwards.

`power_servers` selects hibernate, wake or reboot. Hibernation retains storage costs. Replacement retires the old machine without retaining its disk.

For a machine the user owns, `get_byod_token` returns an expiring, single-use token and install command to run on that machine. The public action uses Kinesis Remote Reach and returns `needVpn: true`; it has no connection-mode or SSH-management input options. The portal separately guides users through Direct or Remote Reach setup. Remote Reach carries TCP app traffic over outbound UDP and does not support UDP app endpoints.

`list_smart_lifecycle` and `set_smart_lifecycle` control automatic idle hibernation and wake-up on owned servers. For SSH, use `list_ssh_keys`, `add_ssh_key` and `assign_ssh_key` with `action: "assign"` or `"unassign"`. Assign only to `eligibleServers` returned by the listing. Remote Reach machines and rented marketplace machines are not eligible. Call `servers_write`; the inner action belongs inside `params`:

```json
{
  "action": "assign_ssh_key",
  "params": {
    "action": "assign",
    "org": "acme",
    "keyId": "<key-id>",
    "addresses": ["<server-address>"]
  }
}
```

For grids, `set_server_priority` accepts -100 to 100; -100 is backup only. `control_grid_app` lets an admin of the grid’s organization start/stop a workload using its runtime `appGuid`. Starting also requires `compute:spend` and bills the app’s organization; its creator receives a notification with the reason.

## Billing and invoices

Use the `billing` tool. `get_balance` and `get_costs` show the selected organization’s balance and costs; billing requires an admin role and `org`; `list_charges` and `list_credits` expose ledger history. `list_invoices` and `get_invoice` return invoice data. Invoice PDF download is REST-only.

MCP does not expose `add_credits` or `set_auto_reload`. On a balance refusal, explain the entitlement and leave billing to an organization admin. MCP errors omit payment links. REST retains checkout sessions, saved-card top-ups and auto-reload updates. Use `billing_write` for `set_default_payment_method` and `remove_payment_method`. Removing the final card disables auto-reload; removal requires `confirm: true`.

`billing_write` → `redeem_voucher` applies a code. `get_node_protection` and `set_node_protection` control balance-triggered hibernation; enabling requires `thresholdHours` from 2 to 48. Node protection depends on remaining balance; Smart Lifecycle depends on idle servers.

## Understand tool annotations

Read tools carry `readOnlyHint: true`. Write tools carry `readOnlyHint: false`; `destructiveHint: true` includes changes to existing state, such as updates, stops, token replacement, notification changes and accepted sharing roles. It does not mean every such action takes `confirm`. The separate `confirm: true` argument is required only where the action schema says so. `openWorldHint` covers public publishing or reaching people beyond Kinesis: deployment/start and domain actions, and `invite_member`, may carry it. Read each action’s annotations rather than inferring them from its name. Clients use these hints to decide how to present approvals; scopes and resource roles still govern execution.

## Stop, delete and retry

Stop apps and poll until stopped before deleting them or their project. A Developer deleting their project must also have created every app inside it; otherwise an admin must act. The current actions requiring `confirm: true` are `delete_project`, `delete_app`, `delete_grid`, `deprovision_servers`, `replace_server`, `clear_logs`, `remove_payment_method`, `remove_member` and `transfer_ownership`. Set it when the user requested or confirmed the action.

Tool failures set `isError: true` and return `structuredContent.error` with a stable `code`, message and code-specific details. MCP tool errors include `requestId` only for server-side failures (status 500 or above), not validation, permission or other client errors. REST error bodies and the HTTP `X-Request-Id` header retain request IDs. Compact action-schema failures include the full `inputSchema` and field paths under `params`. Authentication and rate limits can fail at HTTP level.

Retry transient errors only when safe, reusing an `idempotencyKey` with unchanged arguments where supported. Inspect resources after an uncertain create. Repeating `restart_app` or `trigger_build` performs another restart or build. Fix validation, permissions, placement and balance before retrying.


# Tool reference

Every Kinesis MCP action, with organization context, permissions and complete schemas.

The catalog has **95 actions**. Default compact mode exposes **18 tools**: seven read/write pairs, read-only `catalog`, and `browse_github`, `read_docs` and `describe_action`. With `?mode=all`, each action below is a directly callable tool.

Organization actions use `account` / `account_write`; member actions use `sharing` / `sharing_write`. Organization-level calls take `org` inside `params`. `update_project` replaces `rename_project`; new sharing uses `add_grantees` and `invite_member` instead of `invite_user`.

| Reference                                                      | Actions                                                                                                                                                                                                        |
| -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Identity and built-in docs](/mcp/tools/account-docs)          | `whoami`, `read_docs`                                                                                                                                                                                          |
| [Organizations](/mcp/tools/organizations)                      | `list_orgs`, `get_org`, `create_org`, `update_org`, `list_audit_log`, `list_org_invitations`, `answer_org_invitation`                                                                                          |
| [Members and invitations](/mcp/tools/members)                  | `list_members`, `set_member_role`, `remove_member`, `transfer_ownership`, `list_member_invitations`, `invite_member`, `revoke_member_invitation`                                                               |
| [Projects](/mcp/tools/projects)                                | `list_projects`, `create_project`, `update_project`, `delete_project`                                                                                                                                          |
| [Apps and templates](/mcp/tools/apps)                          | `list_apps`, `list_templates`, `get_app`, `check_placement`, `create_app`, `update_app`, `move_app`                                                                                                            |
| [App lifecycle](/mcp/tools/lifecycle)                          | `get_app_status`, `start_app`, `stop_app`, `restart_app`, `delete_app`                                                                                                                                         |
| [Logs and usage](/mcp/tools/observability)                     | `get_app_logs`, `get_app_usage`, `export_logs`, `get_log_retention`, `set_log_retention`, `clear_logs`                                                                                                         |
| [Builds](/mcp/tools/builds)                                    | `list_builds`, `get_build`, `get_build_logs`, `trigger_build`, `check_rollback_build`, `rollback_build`                                                                                                        |
| [Custom domains](/mcp/tools/domains)                           | `manage_domain`                                                                                                                                                                                                |
| [GitHub](/mcp/tools/github)                                    | `browse_github`                                                                                                                                                                                                |
| [Grids](/mcp/tools/grids)                                      | `list_grids`, `create_grid`, `update_grid`, `set_server_priority`, `control_grid_app`, `delete_grid`                                                                                                           |
| [Servers and orders](/mcp/tools/servers)                       | `list_servers`, `get_server`, `get_server_logs`, `provision_servers`, `deprovision_servers`, `power_servers`, `replace_server`, `get_provision_order`, `manage_provision_order`                                |
| [Catalog and marketplace](/mcp/tools/catalog)                  | `browse_catalog`, `browse_marketplace`                                                                                                                                                                         |
| [Billing](/mcp/tools/billing)                                  | `get_balance`, `get_costs`                                                                                                                                                                                     |
| [Notifications](/mcp/tools/notifications)                      | `list_notifications`, `mark_notifications_seen`, `dismiss_notifications`                                                                                                                                       |
| [Sharing and invitations](/mcp/tools/sharing)                  | `add_grantees`, `list_grantees`, `set_grantee_role`, `revoke_access`, `leave_shared_resource`, `list_invitations`, `cancel_invitation`, `list_received_invitations`, `accept_invitation`, `decline_invitation` |
| [Server setup and SSH](/mcp/tools/server-setup)                | `get_byod_token`, `list_smart_lifecycle`, `set_smart_lifecycle`, `list_ssh_keys`, `add_ssh_key`, `remove_ssh_key`, `assign_ssh_key`                                                                            |
| [Invoices and payment settings](/mcp/tools/billing-management) | `list_invoices`, `get_invoice`, `list_charges`, `list_credits`, `list_payment_methods`, `set_default_payment_method`, `remove_payment_method`, `redeem_voucher`, `get_node_protection`, `set_node_protection`  |

The [OpenAI profile](/mcp/openai-profile) exposes 94 individual tools, omits BYOD token creation, and restricts credential fields.

Read each action’s compact tool mapping, arguments, annotations, scopes and response schema below. `describe_action` supplies the full schema for actions in the selected groups. A tool being listed does not grant its scopes or the required organization role.

[Connect an MCP client](/mcp/connect) · [Agent workflows](/mcp/workflows)


# Identity and built-in docs

Inspect the connected identity and read the platform guides bundled with the server.

Inspect the connected identity and read the platform guides bundled with the server.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `whoami`

Identify the caller: account, credential and permissions. The credential is an API key or a connected app

**Group:** `account` · **Behavior:** Read

**Compact tool:** `account` · **Action:** `whoami`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation     | REST      | Required scopes                                                             |
| ------------- | --------- | --------------------------------------------------------------------------- |
| `auth.whoami` | `GET /me` | Authenticated connection; organization and resource permissions still apply |

### Arguments

No arguments. Send an empty object.

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

</details>

### Result

* `user` (`object`): See the complete response schema below.
* `limitedToOrgs` (`array<object> or null`): The organizations the key or connection is limited to; null when it reaches every one the user is in, including ones they join later (orgs.list has them, with the user's role in each)
* `apiKey` (`object or null`): The key in use, or null when the caller is not using an API key
* `oauthClient` (`object or null`): The app connected through OAuth (hosted chat connectors), or null

<details>

<summary>Response schema: auth.whoami</summary>

```json
{
  "type": "object",
  "properties": {
    "user": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "email",
        "name"
      ],
      "additionalProperties": false
    },
    "limitedToOrgs": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "slug",
              "name"
            ],
            "additionalProperties": false
          }
        },
        {
          "type": "null"
        }
      ],
      "description": "The organizations the key or connection is limited to; null when it reaches every one the user is in, including ones they join later (orgs.list has them, with the user's role in each)"
    },
    "apiKey": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            },
            "name": {
              "type": "string"
            },
            "scopes": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Granted permissions; [\"*\"] is full access, including permissions added to the API later"
            },
            "projectIds": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Projects the key is limited to; empty means every project"
            }
          },
          "required": [
            "id",
            "name",
            "scopes",
            "projectIds"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "The key in use, or null when the caller is not using an API key"
    },
    "oauthClient": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "grantId": {
              "type": "string",
              "description": "The connection as listed in the dashboard's connected apps"
            },
            "clientId": {
              "type": "string"
            },
            "clientName": {
              "type": "string"
            },
            "scopes": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Permissions this connection was authorized with"
            }
          },
          "required": [
            "grantId",
            "clientId",
            "clientName",
            "scopes"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "The app connected through OAuth (hosted chat connectors), or null"
    }
  },
  "required": [
    "user",
    "limitedToOrgs",
    "apiKey",
    "oauthClient"
  ],
  "additionalProperties": false
}
```

</details>

## `read_docs`

The platform guide for agents, by topic; the topic list says what each covers. Read it before an unfamiliar task.

**Group:** `docs` · **Behavior:** Read

**Compact tool:** `read_docs` (direct call)

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation  | REST                | Required scopes                                                             |
| ---------- | ------------------- | --------------------------------------------------------------------------- |
| `docs.get` | `GET /docs/{topic}` | Authenticated connection; organization and resource permissions still apply |

### Arguments

| Argument | Type                                                                                                                         | Required | Description                                                                                                                                                                                                                                               |
| -------- | ---------------------------------------------------------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `topic`  | `"getting_started" / "deploy" / "operate" / "troubleshoot" / "servers" / "billing" / "organizations" / "sharing" / "errors"` | Yes      | getting\_started (concepts, ids, permissions), deploy (create\_app fields), operate (updates, lifecycle, logs), troubleshoot (states and failures), servers (catalog, provisioning, grids), sharing (grantees, invitations), billing, errors (every code) |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "topic": {
      "type": "string",
      "enum": [
        "getting_started",
        "deploy",
        "operate",
        "troubleshoot",
        "servers",
        "billing",
        "organizations",
        "sharing",
        "errors"
      ],
      "description": "getting_started (concepts, ids, permissions), deploy (create_app fields), operate (updates, lifecycle, logs), troubleshoot (states and failures), servers (catalog, provisioning, grids), sharing (grantees, invitations), billing, errors (every code)"
    }
  },
  "required": [
    "topic"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `topic` (`"getting_started" / "deploy" / "operate" / "troubleshoot" / "servers" / "billing" / "organizations" / "sharing" / "errors"`): getting\_started (concepts, ids, permissions), deploy (create\_app fields), operate (updates, lifecycle, logs), troubleshoot (states and failures), servers (catalog, provisioning, grids), sharing (grantees, invitations), billing, errors (every code)
* `title` (`string`): See the complete response schema below.
* `markdown` (`string`): The guide text, in markdown

<details>

<summary>Response schema: docs.get</summary>

```json
{
  "type": "object",
  "properties": {
    "topic": {
      "type": "string",
      "enum": [
        "getting_started",
        "deploy",
        "operate",
        "troubleshoot",
        "servers",
        "billing",
        "organizations",
        "sharing",
        "errors"
      ],
      "description": "getting_started (concepts, ids, permissions), deploy (create_app fields), operate (updates, lifecycle, logs), troubleshoot (states and failures), servers (catalog, provisioning, grids), sharing (grantees, invitations), billing, errors (every code)"
    },
    "title": {
      "type": "string"
    },
    "markdown": {
      "type": "string",
      "description": "The guide text, in markdown"
    }
  },
  "required": [
    "topic",
    "title",
    "markdown"
  ],
  "additionalProperties": false
}
```

</details>


# Organizations

Discover organizations, manage their settings and audit log, and answer invitations to join.

Discover organizations, manage their settings and audit log, and answer invitations to join.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

These actions use `account` and `account_write` in compact mode. `audit:read` is a separate permission: `org:write` does not imply it. Credentials limited to organizations or projects cannot create an organization or answer a received invitation.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `list_orgs`

The organizations the caller can act in, by name, with their role in each. Calls in an organization take its slug as org when there is more than one

**Group:** `account` · **Behavior:** Read

**Compact tool:** `account` · **Action:** `list_orgs`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation   | REST        | Required scopes                                                             |
| ----------- | ----------- | --------------------------------------------------------------------------- |
| `orgs.list` | `GET /orgs` | Authenticated connection; organization and resource permissions still apply |

### Arguments

No arguments. Send an empty object.

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

</details>

### Result

* `orgs` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: orgs.list</summary>

```json
{
  "type": "object",
  "properties": {
    "orgs": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "description": "What calls in an organization take as org"
          },
          "name": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "admin",
              "developer",
              "viewer"
            ],
            "description": "The caller's role there, which bounds what they can do in it"
          }
        },
        "required": [
          "id",
          "slug",
          "name",
          "role"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "orgs"
  ],
  "additionalProperties": false
}
```

</details>

## `get_org`

An organization's settings, whether it is frozen, and the caller's role in it

**Group:** `account` · **Behavior:** Read

**Compact tool:** `account` · **Action:** `get_org`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation  | REST       | Required scopes                                                             |
| ---------- | ---------- | --------------------------------------------------------------------------- |
| `orgs.get` | `GET /org` | Authenticated connection; organization and resource permissions still apply |

### Arguments

| Argument | Type     | Required | Description                                                                                      |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `org`    | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): See the complete response schema below.
* `slug` (`string`): What calls in an organization take as org
* `name` (`string`): See the complete response schema below.
* `role` (`"admin" / "developer" / "viewer"`): The caller's role there, which bounds what they can do in it
* `isOwner` (`boolean`): The caller owns it (members.list names the owner)
* `settings` (`object`): See the complete response schema below.
* `frozen` (`boolean`): Frozen over a payment problem: it can't start apps, add servers or pay
* `createdAt` (`string`): ISO 8601 timestamp (UTC)

<details>

<summary>Response schema: orgs.get</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "slug": {
      "type": "string",
      "description": "What calls in an organization take as org"
    },
    "name": {
      "type": "string"
    },
    "role": {
      "type": "string",
      "enum": [
        "admin",
        "developer",
        "viewer"
      ],
      "description": "The caller's role there, which bounds what they can do in it"
    },
    "isOwner": {
      "type": "boolean",
      "description": "The caller owns it (members.list names the owner)"
    },
    "settings": {
      "type": "object",
      "properties": {
        "serverlessEnabled": {
          "type": "boolean",
          "description": "Developers may run apps on Kinesis serverless capacity, billed to the organization; admins always can"
        },
        "membersCanInvite": {
          "type": "boolean",
          "description": "Developers may invite people, as developers or viewers; admins always can"
        }
      },
      "required": [
        "serverlessEnabled",
        "membersCanInvite"
      ],
      "additionalProperties": false
    },
    "frozen": {
      "type": "boolean",
      "description": "Frozen over a payment problem: it can't start apps, add servers or pay"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    }
  },
  "required": [
    "id",
    "slug",
    "name",
    "role",
    "isOwner",
    "settings",
    "frozen",
    "createdAt"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`.

## `create_org`

Create an organization; the caller becomes its owner and admin. A person owns up to 5. A key or connection limited to some organizations or projects can't, since it would not reach the new one.

**Group:** `account` · **Behavior:** Write

**Compact tool:** `account_write` · **Action:** `create_org`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation     | REST         | Required scopes |
| ------------- | ------------ | --------------- |
| `orgs.create` | `POST /orgs` | `org:write`     |

### Arguments

| Argument         | Type     | Required | Description                                                                                                                                                           |
| ---------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`           | `string` | Yes      | The organization's name                                                                                                                                               |
| `slug`           | `string` | No       | Its name in URLs and as org; lowercase letters, digits and dashes. Made from the name when omitted                                                                    |
| `idempotencyKey` | `string` | No       | Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80,
      "description": "The organization's name"
    },
    "slug": {
      "type": "string",
      "minLength": 3,
      "maxLength": 39,
      "description": "Its name in URLs and as org; lowercase letters, digits and dashes. Made from the name when omitted"
    },
    "idempotencyKey": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "pattern": "^\\S+$",
      "description": "Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice"
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): See the complete response schema below.
* `slug` (`string`): What calls in an organization take as org
* `name` (`string`): See the complete response schema below.
* `role` (`"admin" / "developer" / "viewer"`): The caller's role there, which bounds what they can do in it
* `isOwner` (`boolean`): The caller owns it (members.list names the owner)
* `settings` (`object`): See the complete response schema below.
* `frozen` (`boolean`): Frozen over a payment problem: it can't start apps, add servers or pay
* `createdAt` (`string`): ISO 8601 timestamp (UTC)

<details>

<summary>Response schema: orgs.create</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "slug": {
      "type": "string",
      "description": "What calls in an organization take as org"
    },
    "name": {
      "type": "string"
    },
    "role": {
      "type": "string",
      "enum": [
        "admin",
        "developer",
        "viewer"
      ],
      "description": "The caller's role there, which bounds what they can do in it"
    },
    "isOwner": {
      "type": "boolean",
      "description": "The caller owns it (members.list names the owner)"
    },
    "settings": {
      "type": "object",
      "properties": {
        "serverlessEnabled": {
          "type": "boolean",
          "description": "Developers may run apps on Kinesis serverless capacity, billed to the organization; admins always can"
        },
        "membersCanInvite": {
          "type": "boolean",
          "description": "Developers may invite people, as developers or viewers; admins always can"
        }
      },
      "required": [
        "serverlessEnabled",
        "membersCanInvite"
      ],
      "additionalProperties": false
    },
    "frozen": {
      "type": "boolean",
      "description": "Frozen over a payment problem: it can't start apps, add servers or pay"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    }
  },
  "required": [
    "id",
    "slug",
    "name",
    "role",
    "isOwner",
    "settings",
    "frozen",
    "createdAt"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 invalid_slug`, `409 slug_taken`, `409 org_limit_reached`, `403 key_project_restricted`.

## `update_org`

Rename an organization or change its settings (admins). Its slug never changes

**Group:** `account` · **Behavior:** Write

**Compact tool:** `account_write` · **Action:** `update_org`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation     | REST         | Required scopes |
| ------------- | ------------ | --------------- |
| `orgs.update` | `PATCH /org` | `org:write`     |

### Arguments

| Argument            | Type      | Required | Description                                                                                           |
| ------------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------- |
| `name`              | `string`  | No       | The organization's name                                                                               |
| `serverlessEnabled` | `boolean` | No       | Developers may run apps on Kinesis serverless capacity, billed to the organization; admins always can |
| `membersCanInvite`  | `boolean` | No       | Developers may invite people, as developers or viewers; admins always can                             |
| `org`               | `string`  | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one      |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80,
      "description": "The organization's name"
    },
    "serverlessEnabled": {
      "type": "boolean",
      "description": "Developers may run apps on Kinesis serverless capacity, billed to the organization; admins always can"
    },
    "membersCanInvite": {
      "type": "boolean",
      "description": "Developers may invite people, as developers or viewers; admins always can"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): See the complete response schema below.
* `slug` (`string`): What calls in an organization take as org
* `name` (`string`): See the complete response schema below.
* `role` (`"admin" / "developer" / "viewer"`): The caller's role there, which bounds what they can do in it
* `isOwner` (`boolean`): The caller owns it (members.list names the owner)
* `settings` (`object`): See the complete response schema below.
* `frozen` (`boolean`): Frozen over a payment problem: it can't start apps, add servers or pay
* `createdAt` (`string`): ISO 8601 timestamp (UTC)

<details>

<summary>Response schema: orgs.update</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string"
    },
    "slug": {
      "type": "string",
      "description": "What calls in an organization take as org"
    },
    "name": {
      "type": "string"
    },
    "role": {
      "type": "string",
      "enum": [
        "admin",
        "developer",
        "viewer"
      ],
      "description": "The caller's role there, which bounds what they can do in it"
    },
    "isOwner": {
      "type": "boolean",
      "description": "The caller owns it (members.list names the owner)"
    },
    "settings": {
      "type": "object",
      "properties": {
        "serverlessEnabled": {
          "type": "boolean",
          "description": "Developers may run apps on Kinesis serverless capacity, billed to the organization; admins always can"
        },
        "membersCanInvite": {
          "type": "boolean",
          "description": "Developers may invite people, as developers or viewers; admins always can"
        }
      },
      "required": [
        "serverlessEnabled",
        "membersCanInvite"
      ],
      "additionalProperties": false
    },
    "frozen": {
      "type": "boolean",
      "description": "Frozen over a payment problem: it can't start apps, add servers or pay"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    }
  },
  "required": [
    "id",
    "slug",
    "name",
    "role",
    "isOwner",
    "settings",
    "frozen",
    "createdAt"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `list_audit_log`

The organization's audit log, latest recorded first (admins). Who did what to its projects, apps, grids, servers, members and settings, kept for a year

**Group:** `account` · **Behavior:** Read

**Compact tool:** `account` · **Action:** `list_audit_log`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation         | REST             | Required scopes |
| ----------------- | ---------------- | --------------- |
| `orgs.audit.list` | `GET /org/audit` | `audit:read`    |

### Arguments

| Argument | Type      | Required | Description                                                                                      |
| -------- | --------- | -------- | ------------------------------------------------------------------------------------------------ |
| `limit`  | `integer` | No       | Items per page, 1 to 200 (default 50)                                                            |
| `cursor` | `string`  | No       | nextCursor from the previous page; omit for the first page                                       |
| `org`    | `string`  | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 200,
      "description": "Items per page, 1 to 200 (default 50)"
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "nextCursor from the previous page; omit for the first page"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `entries` (`array<object>`): See the complete response schema below.
* `nextCursor` (`string or null`): Pass as cursor to fetch the next page; null on the last page

<details>

<summary>Response schema: orgs.audit.list</summary>

```json
{
  "type": "object",
  "properties": {
    "entries": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "When it happened"
          },
          "actor": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string",
                    "description": "User id, as members.list and the sharing calls use it"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "email": {
                    "type": "string"
                  }
                },
                "required": [
                  "userId",
                  "name",
                  "email"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Who did it; null for the platform, or an account that is gone"
          },
          "viaSupport": {
            "type": "boolean",
            "description": "Kinesis support did it while signed in as the actor"
          },
          "role": {
            "description": "The actor's role at the time",
            "type": [
              "string",
              "null"
            ]
          },
          "resourceType": {
            "type": "string",
            "description": "project, app, pool (a grid) or org (its members, settings and servers)"
          },
          "resourceId": {
            "description": "Project or app id, grid id, or the organization's id",
            "type": [
              "string",
              "null"
            ]
          },
          "resourceName": {
            "description": "The project's or app's name, while it exists",
            "type": [
              "string",
              "null"
            ]
          },
          "action": {
            "type": "string",
            "description": "What was done, e.g. appCreate, appStop, memberInvite, serverProvision"
          },
          "outcome": {
            "type": "string",
            "enum": [
              "success",
              "error"
            ]
          },
          "errorCode": {
            "description": "Why it failed, as a code; the stored message can name internals",
            "type": [
              "string",
              "null"
            ]
          },
          "details": {
            "anyOf": [
              {
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {}
              },
              {
                "type": "null"
              }
            ],
            "description": "What the action concerned: an app's name, a member's email and role, server addresses, ..."
          },
          "changes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string",
                  "description": "The field, e.g. name or envVars.API_KEY"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "added",
                    "removed",
                    "changed"
                  ]
                },
                "before": {},
                "after": {},
                "redacted": {
                  "description": "A secret: its values are not kept",
                  "type": "boolean"
                }
              },
              "required": [
                "path",
                "kind"
              ],
              "additionalProperties": false
            },
            "description": "Field-level changes, for actions that edit fields"
          },
          "message": {
            "description": "For something the platform did: what it reported",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "at",
          "actor",
          "viaSupport",
          "role",
          "resourceType",
          "resourceId",
          "resourceName",
          "action",
          "outcome",
          "errorCode",
          "details",
          "changes",
          "message"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": {
      "description": "Pass as cursor to fetch the next page; null on the last page",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "entries",
    "nextCursor"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `list_org_invitations`

Open invitations to join an organization, addressed to the caller's email

**Group:** `account` · **Behavior:** Read

**Compact tool:** `account` · **Action:** `list_org_invitations`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                   | REST                    | Required scopes |
| --------------------------- | ----------------------- | --------------- |
| `orgs.invitations.received` | `GET /orgs/invitations` | `account:read`  |

### Arguments

No arguments. Send an empty object.

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

</details>

### Result

* `invitations` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: orgs.invitations.received</summary>

```json
{
  "type": "object",
  "properties": {
    "invitations": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Invitation id"
          },
          "org": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "slug",
              "name"
            ],
            "additionalProperties": false
          },
          "role": {
            "type": "string",
            "enum": [
              "admin",
              "developer",
              "viewer"
            ],
            "description": "The role the caller would join with"
          },
          "invitedBy": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string",
                    "description": "User id, as members.list and the sharing calls use it"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "email": {
                    "type": "string"
                  }
                },
                "required": [
                  "userId",
                  "name",
                  "email"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          }
        },
        "required": [
          "id",
          "org",
          "role",
          "invitedBy",
          "expiresAt"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "invitations"
  ],
  "additionalProperties": false
}
```

</details>

## `answer_org_invitation`

Answer an invitation to an organization. action=accept joins it with the invited role; action=decline closes the invitation.

**Group:** `account` · **Behavior:** Write

**Compact tool:** `account_write` · **Action:** `answer_org_invitation`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                  | REST                                            | Required scopes |
| -------------------------- | ----------------------------------------------- | --------------- |
| `orgs.invitations.accept`  | `POST /orgs/invitations/{invitationId}/accept`  | `account:write` |
| `orgs.invitations.decline` | `POST /orgs/invitations/{invitationId}/decline` | `account:write` |

### Arguments

| Argument       | Type                   | Required | Description                                   |
| -------------- | ---------------------- | -------- | --------------------------------------------- |
| `action`       | `"accept" / "decline"` | Yes      | Join, or turn it down                         |
| `invitationId` | `string`               | Yes      | Invitation id, from orgs.invitations.received |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "accept",
        "decline"
      ],
      "description": "Join, or turn it down"
    },
    "invitationId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Invitation id, from orgs.invitations.received"
    }
  },
  "required": [
    "action",
    "invitationId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

**`orgs.invitations.accept`**

* `org` (`object`): See the complete response schema below.
* `joined` (`boolean`): False when the caller was a member already and keeps their role

<details>

<summary>Response schema: orgs.invitations.accept</summary>

```json
{
  "type": "object",
  "properties": {
    "org": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "slug": {
          "type": "string",
          "description": "What calls in an organization take as org"
        },
        "name": {
          "type": "string"
        },
        "role": {
          "type": "string",
          "enum": [
            "admin",
            "developer",
            "viewer"
          ],
          "description": "The caller's role there, which bounds what they can do in it"
        }
      },
      "required": [
        "id",
        "slug",
        "name",
        "role"
      ],
      "additionalProperties": false
    },
    "joined": {
      "type": "boolean",
      "description": "False when the caller was a member already and keeps their role"
    }
  },
  "required": [
    "org",
    "joined"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 invitation_closed`, `403 key_project_restricted`. **`orgs.invitations.decline`**

* `declined` (`true`): See the complete response schema below.

<details>

<summary>Response schema: orgs.invitations.decline</summary>

```json
{
  "type": "object",
  "properties": {
    "declined": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "declined"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 invitation_closed`, `403 key_project_restricted`.


# Members and invitations

List organization members, manage their roles and ownership, and invite people to join.

List organization members, manage their roles and ownership, and invite people to join.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

These actions use `sharing` and `sharing_write` in compact mode. Organization roles are `admin`, `developer` and `viewer`. Removing a member ends access in this organization; resources remain and apps keep running. Only the owner can transfer ownership. `details: true` adds member activity and resource counts only for admins using a credential that is not limited to projects.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `list_members`

The members of an organization and their roles, admins first. These are the people add\_grantees can add to its projects, apps and grids

**Group:** `sharing` · **Behavior:** Read

**Compact tool:** `sharing` · **Action:** `list_members`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation      | REST           | Required scopes |
| -------------- | -------------- | --------------- |
| `members.list` | `GET /members` | `members:read`  |

### Arguments

| Argument  | Type      | Required | Description                                                                                      |
| --------- | --------- | -------- | ------------------------------------------------------------------------------------------------ |
| `details` | `boolean` | No       | For admins: also each member's last sign-in and what they created (lastSignInAt, created)        |
| `org`     | `string`  | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "details": {
      "type": "boolean",
      "description": "For admins: also each member's last sign-in and what they created (lastSignInAt, created)"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `members` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: members.list</summary>

```json
{
  "type": "object",
  "properties": {
    "members": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "What add_grantees and the member calls take"
          },
          "email": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "admin",
              "developer",
              "viewer"
            ]
          },
          "joinedAt": {
            "type": "string"
          },
          "isOwner": {
            "type": "boolean",
            "description": "Owns the organization: always an admin, and stays until they transfer ownership"
          },
          "lastSignInAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ],
            "description": "With details, for admins: when they last signed in"
          },
          "created": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "projects": {
                    "type": "integer"
                  },
                  "apps": {
                    "type": "integer"
                  },
                  "grids": {
                    "type": "integer"
                  }
                },
                "required": [
                  "projects",
                  "apps",
                  "grids"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "With details, for admins: what they created here, which stays when they leave"
          }
        },
        "required": [
          "userId",
          "email",
          "name",
          "role",
          "joinedAt",
          "isOwner",
          "lastSignInAt",
          "created"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "members"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`.

## `set_member_role`

Change a member's role (admins). Their invitations above the new role are withdrawn

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `set_member_role`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation        | REST                      | Required scopes |
| ---------------- | ------------------------- | --------------- |
| `members.update` | `PATCH /members/{userId}` | `members:write` |

### Arguments

| Argument | Type                               | Required | Description                                                                                      |
| -------- | ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `role`   | `"admin" / "developer" / "viewer"` | Yes      | Their new role                                                                                   |
| `userId` | `string`                           | Yes      | The member's user id, from members.list                                                          |
| `org`    | `string`                           | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "role": {
      "type": "string",
      "enum": [
        "admin",
        "developer",
        "viewer"
      ],
      "description": "Their new role"
    },
    "userId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "The member's user id, from members.list"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "role",
    "userId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `userId` (`string`): See the complete response schema below.
* `role` (`"admin" / "developer" / "viewer"`): See the complete response schema below.

<details>

<summary>Response schema: members.update</summary>

```json
{
  "type": "object",
  "properties": {
    "userId": {
      "type": "string"
    },
    "role": {
      "type": "string",
      "enum": [
        "admin",
        "developer",
        "viewer"
      ]
    }
  },
  "required": [
    "userId",
    "role"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 last_admin`, `409 owner`, `409 role_changed`, `400 org_required`, `403 key_project_restricted`.

## `remove_member`

Remove a member (admins), or leave with the caller's own user id. Their access, keys and connected apps in the organization end; what they created stays and their apps keep running.

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `remove_member`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

Requires `confirm: true` for this irreversible action.

### Permissions and REST equivalents

| Operation        | REST                       | Required scopes |
| ---------------- | -------------------------- | --------------- |
| `members.remove` | `DELETE /members/{userId}` | `members:write` |

### Arguments

| Argument  | Type     | Required | Description                                                                                      |
| --------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `userId`  | `string` | Yes      | The member's user id, from members.list                                                          |
| `confirm` | `true`   | Yes      | Must be true: this action cannot be undone                                                       |
| `org`     | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "userId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "The member's user id, from members.list"
    },
    "confirm": {
      "type": "boolean",
      "const": true,
      "description": "Must be true: this action cannot be undone"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "userId",
    "confirm"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `userId` (`string`): See the complete response schema below.
* `removed` (`true`): See the complete response schema below.
* `warning` (`string or null`): Set when a grid they had contributed to could not be fully released

<details>

<summary>Response schema: members.remove</summary>

```json
{
  "type": "object",
  "properties": {
    "userId": {
      "type": "string"
    },
    "removed": {
      "type": "boolean",
      "const": true
    },
    "warning": {
      "description": "Set when a grid they had contributed to could not be fully released",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "userId",
    "removed",
    "warning"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 last_admin`, `409 owner`, `400 org_required`, `403 key_project_restricted`.

## `transfer_ownership`

Hand the organization to another member (its owner only). They become an admin if they aren't one; the previous owner stays an admin.

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `transfer_ownership`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

Requires `confirm: true` for this irreversible action.

### Permissions and REST equivalents

| Operation                | REST                  | Required scopes |
| ------------------------ | --------------------- | --------------- |
| `members.owner.transfer` | `POST /members/owner` | `members:write` |

### Arguments

| Argument  | Type     | Required | Description                                                                                      |
| --------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `userId`  | `string` | Yes      | The member's user id, from members.list                                                          |
| `confirm` | `true`   | Yes      | Must be true: this action cannot be undone                                                       |
| `org`     | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "userId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "The member's user id, from members.list"
    },
    "confirm": {
      "type": "boolean",
      "const": true,
      "description": "Must be true: this action cannot be undone"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "userId",
    "confirm"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `ownerUserId` (`string`): The new owner

<details>

<summary>Response schema: members.owner.transfer</summary>

```json
{
  "type": "object",
  "properties": {
    "ownerUserId": {
      "type": "string",
      "description": "The new owner"
    }
  },
  "required": [
    "ownerUserId"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 already_owner`, `409 owner_changed`, `409 role_changed`, `400 org_required`, `403 key_project_restricted`.

## `list_member_invitations`

Pending invitations to the organization, newest first. Admins see every one; a developer the ones they sent

**Group:** `sharing` · **Behavior:** Read

**Compact tool:** `sharing` · **Action:** `list_member_invitations`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                  | REST                       | Required scopes |
| -------------------------- | -------------------------- | --------------- |
| `members.invitations.list` | `GET /members/invitations` | `members:read`  |

### Arguments

| Argument | Type     | Required | Description                                                                                      |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `org`    | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `invitations` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: members.invitations.list</summary>

```json
{
  "type": "object",
  "properties": {
    "invitations": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Invitation id"
          },
          "email": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "admin",
              "developer",
              "viewer"
            ],
            "description": "The role they join with"
          },
          "invitedBy": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string",
                    "description": "User id, as members.list and the sharing calls use it"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "email": {
                    "type": "string"
                  }
                },
                "required": [
                  "userId",
                  "name",
                  "email"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          },
          "expired": {
            "type": "boolean",
            "description": "Past its date; inviting the address again sends a fresh link"
          },
          "addsTo": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Names of the projects, apps and grids they are added to on joining"
          }
        },
        "required": [
          "id",
          "email",
          "role",
          "invitedBy",
          "expiresAt",
          "expired",
          "addsTo"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "invitations"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `invite_member`

Invite someone to the organization by email, at the caller's role or below. They get a link good for 7 days; inviting an address again sends a fresh one. addTo also adds them to a project, app or grid on joining.

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `invite_member`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true`

### Permissions and REST equivalents

| Operation                    | REST                          | Required scopes  |
| ---------------------------- | ----------------------------- | ---------------- |
| `members.invitations.create` | `POST /members/invitations`   | `members:write`  |
| Conditional                  | addTo.resourceType is project | `projects:admin` |
| Conditional                  | addTo.resourceType is app     | `apps:admin`     |
| Conditional                  | addTo.resourceType is grid    | `pools:admin`    |

### Arguments

| Argument | Type                               | Required | Description                                                                                      |
| -------- | ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `email`  | `string`                           | Yes      | Who to invite                                                                                    |
| `role`   | `"admin" / "developer" / "viewer"` | Yes      | The role they join with: admin, developer or viewer                                              |
| `addTo`  | `object`                           | No       | A project, app or grid of the organization the caller may add people to                          |
| `org`    | `string`                           | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "minLength": 1,
      "maxLength": 320,
      "description": "Who to invite"
    },
    "role": {
      "type": "string",
      "enum": [
        "admin",
        "developer",
        "viewer"
      ],
      "description": "The role they join with: admin, developer or viewer"
    },
    "addTo": {
      "type": "object",
      "properties": {
        "resourceType": {
          "type": "string",
          "enum": [
            "project",
            "app",
            "grid"
          ],
          "description": "What is shared"
        },
        "resourceId": {
          "type": "string",
          "minLength": 1,
          "description": "Project or app id, or grid id"
        }
      },
      "required": [
        "resourceType",
        "resourceId"
      ],
      "additionalProperties": {},
      "description": "A project, app or grid of the organization the caller may add people to"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "email",
    "role"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Invitation id
* `email` (`string`): See the complete response schema below.
* `role` (`"admin" / "developer" / "viewer"`): The role they join with
* `invitedBy` (`object or null`): See the complete response schema below.
* `expiresAt` (`string`): ISO 8601 timestamp (UTC)
* `expired` (`boolean`): Past its date; inviting the address again sends a fresh link
* `addsTo` (`array<string>`): Names of the projects, apps and grids they are added to on joining
* `warning` (`string or null`): Set when the invitation was saved but its email could not be sent

<details>

<summary>Response schema: members.invitations.create</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Invitation id"
    },
    "email": {
      "type": "string"
    },
    "role": {
      "type": "string",
      "enum": [
        "admin",
        "developer",
        "viewer"
      ],
      "description": "The role they join with"
    },
    "invitedBy": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "userId": {
              "type": "string",
              "description": "User id, as members.list and the sharing calls use it"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "email": {
              "type": "string"
            }
          },
          "required": [
            "userId",
            "name",
            "email"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ]
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    },
    "expired": {
      "type": "boolean",
      "description": "Past its date; inviting the address again sends a fresh link"
    },
    "addsTo": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Names of the projects, apps and grids they are added to on joining"
    },
    "warning": {
      "description": "Set when the invitation was saved but its email could not be sent",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "id",
    "email",
    "role",
    "invitedBy",
    "expiresAt",
    "expired",
    "addsTo",
    "warning"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 invalid_email`, `403 invites_admins_only`, `403 role_too_high`, `409 already_member`, `409 invitation_exists`, `429 resend_too_soon`, `429 invitation_limit`, `400 org_required`, `403 key_project_restricted`.

## `revoke_member_invitation`

Withdraw a pending invitation to the organization. Its link stops working

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `revoke_member_invitation`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                    | REST                                         | Required scopes |
| ---------------------------- | -------------------------------------------- | --------------- |
| `members.invitations.revoke` | `DELETE /members/invitations/{invitationId}` | `members:write` |

### Arguments

| Argument       | Type     | Required | Description                                                                                      |
| -------------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `invitationId` | `string` | Yes      | Invitation id, from members.invitations.list                                                     |
| `org`          | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "invitationId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Invitation id, from members.invitations.list"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "invitationId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `invitationId` (`string`): See the complete response schema below.
* `revoked` (`true`): See the complete response schema below.

<details>

<summary>Response schema: members.invitations.revoke</summary>

```json
{
  "type": "object",
  "properties": {
    "invitationId": {
      "type": "string"
    },
    "revoked": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "invitationId",
    "revoked"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.


# Notifications

Read notifications, mark them seen or dismiss them.

Read notifications, mark them seen or dismiss them.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `list_notifications`

The caller's active notifications, newest first, with the unseen count. Dismissed ones are left out

**Group:** `account` · **Behavior:** Read

**Compact tool:** `account` · **Action:** `list_notifications`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation            | REST                 | Required scopes |
| -------------------- | -------------------- | --------------- |
| `notifications.list` | `GET /notifications` | `account:read`  |

### Arguments

| Argument     | Type      | Required | Description                                                |
| ------------ | --------- | -------- | ---------------------------------------------------------- |
| `unseenOnly` | `boolean` | No       | Only notifications not marked seen                         |
| `limit`      | `integer` | No       | Items per page, 1 to 200 (default 50)                      |
| `cursor`     | `string`  | No       | nextCursor from the previous page; omit for the first page |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "unseenOnly": {
      "type": "boolean",
      "description": "Only notifications not marked seen"
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 200,
      "description": "Items per page, 1 to 200 (default 50)"
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "nextCursor from the previous page; omit for the first page"
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `notifications` (`array<object>`): See the complete response schema below.
* `unseenCount` (`integer`): Active notifications not marked seen, across every page
* `nextCursor` (`string or null`): Pass as cursor to fetch the next page; null on the last page

<details>

<summary>Response schema: notifications.list</summary>

```json
{
  "type": "object",
  "properties": {
    "notifications": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Notification id"
          },
          "type": {
            "type": "string",
            "description": "What happened, e.g. share_invitation_received, grid_machines_online"
          },
          "title": {
            "type": "string"
          },
          "message": {
            "type": [
              "string",
              "null"
            ]
          },
          "resourceType": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "project",
                  "app",
                  "grid"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "resourceId": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "description": "Dashboard page the notification points at",
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          },
          "seenAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "resolved": {
            "type": "boolean",
            "description": "Its inline action (accept, decline) has been taken already"
          }
        },
        "required": [
          "id",
          "type",
          "title",
          "message",
          "resourceType",
          "resourceId",
          "url",
          "createdAt",
          "seenAt",
          "resolved"
        ],
        "additionalProperties": false
      }
    },
    "unseenCount": {
      "type": "integer",
      "description": "Active notifications not marked seen, across every page"
    },
    "nextCursor": {
      "description": "Pass as cursor to fetch the next page; null on the last page",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "notifications",
    "unseenCount",
    "nextCursor"
  ],
  "additionalProperties": false
}
```

</details>

## `mark_notifications_seen`

Mark one notification, or every active one, as seen

**Group:** `account` · **Behavior:** Write

**Compact tool:** `account_write` · **Action:** `mark_notifications_seen`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                | REST                       | Required scopes |
| ------------------------ | -------------------------- | --------------- |
| `notifications.markSeen` | `POST /notifications/seen` | `account:write` |

### Arguments

| Argument | Type     | Required | Description                                 |
| -------- | -------- | -------- | ------------------------------------------- |
| `id`     | `string` | No       | One notification; omit for every active one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "One notification; omit for every active one"
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `updated` (`integer`): Notifications that changed

<details>

<summary>Response schema: notifications.markSeen</summary>

```json
{
  "type": "object",
  "properties": {
    "updated": {
      "type": "integer",
      "description": "Notifications that changed"
    }
  },
  "required": [
    "updated"
  ],
  "additionalProperties": false
}
```

</details>

## `dismiss_notifications`

Dismiss one notification, or every active one. A dismissed notification no longer lists

**Group:** `account` · **Behavior:** Write

**Compact tool:** `account_write` · **Action:** `dismiss_notifications`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation               | REST                    | Required scopes |
| ----------------------- | ----------------------- | --------------- |
| `notifications.dismiss` | `DELETE /notifications` | `account:write` |

### Arguments

| Argument | Type     | Required | Description                                 |
| -------- | -------- | -------- | ------------------------------------------- |
| `id`     | `string` | No       | One notification; omit for every active one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "One notification; omit for every active one"
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `dismissed` (`integer`): Notifications dismissed

<details>

<summary>Response schema: notifications.dismiss</summary>

```json
{
  "type": "object",
  "properties": {
    "dismissed": {
      "type": "integer",
      "description": "Notifications dismissed"
    }
  },
  "required": [
    "dismissed"
  ],
  "additionalProperties": false
}
```

</details>


# Projects

Organize applications into projects and manage project lifecycle.

Organize applications into projects and manage project lifecycle.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

`update_project` replaces `rename_project` and can update the name or visibility. Projects belong to an organization. Admins and developers create them; admins or the creator manage access and delete them.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `list_projects`

List projects accessible to the caller, oldest first, across reachable organizations unless org narrows the list

**Group:** `projects` · **Behavior:** Read

**Compact tool:** `projects` · **Action:** `list_projects`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation       | REST            | Required scopes |
| --------------- | --------------- | --------------- |
| `projects.list` | `GET /projects` | `projects:read` |

### Arguments

| Argument | Type      | Required | Description                                                                          |
| -------- | --------- | -------- | ------------------------------------------------------------------------------------ |
| `limit`  | `integer` | No       | Items per page, 1 to 200 (default 50)                                                |
| `cursor` | `string`  | No       | nextCursor from the previous page; omit for the first page                           |
| `org`    | `string`  | No       | Slug of an organization (see orgs.list) to list only what is in it or shared into it |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 200,
      "description": "Items per page, 1 to 200 (default 50)"
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "nextCursor from the previous page; omit for the first page"
    },
    "org": {
      "description": "Slug of an organization (see orgs.list) to list only what is in it or shared into it",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `projects` (`array<object>`): See the complete response schema below.
* `nextCursor` (`string or null`): Pass as cursor to fetch the next page; null on the last page

<details>

<summary>Response schema: projects.list</summary>

```json
{
  "type": "object",
  "properties": {
    "projects": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Project id"
          },
          "name": {
            "type": "string"
          },
          "org": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "slug",
              "name"
            ],
            "additionalProperties": false,
            "description": "The organization that owns it"
          },
          "visibility": {
            "type": "string",
            "enum": [
              "private",
              "org"
            ],
            "description": "private: admins, its creator and the people added; org: everyone in the organization"
          },
          "access": {
            "type": "object",
            "properties": {
              "role": {
                "type": "string",
                "enum": [
                  "admin",
                  "developer",
                  "viewer"
                ],
                "description": "The caller's role on it"
              },
              "outside": {
                "type": "boolean",
                "description": "Shared with the caller from another organization"
              },
              "isCreator": {
                "type": "boolean",
                "description": "The caller created it"
              }
            },
            "required": [
              "role",
              "outside",
              "isCreator"
            ],
            "additionalProperties": false
          },
          "appCount": {
            "type": "integer"
          },
          "createdBy": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string",
                    "description": "User id, as members.list and the sharing calls use it"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "email": {
                    "type": "string"
                  }
                },
                "required": [
                  "userId",
                  "name",
                  "email"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          }
        },
        "required": [
          "id",
          "name",
          "org",
          "visibility",
          "access",
          "appCount",
          "createdBy",
          "createdAt"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": {
      "description": "Pass as cursor to fetch the next page; null on the last page",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "projects",
    "nextCursor"
  ],
  "additionalProperties": false
}
```

</details>

## `create_project`

Create an empty project. It is private unless visibility is org

**Group:** `projects` · **Behavior:** Write

**Compact tool:** `projects_write` · **Action:** `create_project`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation         | REST             | Required scopes  |
| ----------------- | ---------------- | ---------------- |
| `projects.create` | `POST /projects` | `projects:write` |

### Arguments

| Argument         | Type                | Required | Description                                                                                                                                                           |
| ---------------- | ------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`           | `string`            | Yes      | Project name                                                                                                                                                          |
| `visibility`     | `"private" / "org"` | No       | private: admins, its creator and the people added; org: everyone in the organization                                                                                  |
| `idempotencyKey` | `string`            | No       | Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice |
| `org`            | `string`            | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one                                                                      |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "Project name"
    },
    "visibility": {
      "type": "string",
      "enum": [
        "private",
        "org"
      ],
      "description": "private: admins, its creator and the people added; org: everyone in the organization"
    },
    "idempotencyKey": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "pattern": "^\\S+$",
      "description": "Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Project id
* `name` (`string`): See the complete response schema below.
* `org` (`object`): The organization that owns it
* `visibility` (`"private" / "org"`): private: admins, its creator and the people added; org: everyone in the organization
* `access` (`object`): See the complete response schema below.
* `appCount` (`integer`): See the complete response schema below.
* `createdBy` (`object or null`): Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read
* `createdAt` (`string`): ISO 8601 timestamp (UTC)

<details>

<summary>Response schema: projects.create</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Project id"
    },
    "name": {
      "type": "string"
    },
    "org": {
      "type": "object",
      "properties": {
        "slug": {
          "type": "string"
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "slug",
        "name"
      ],
      "additionalProperties": false,
      "description": "The organization that owns it"
    },
    "visibility": {
      "type": "string",
      "enum": [
        "private",
        "org"
      ],
      "description": "private: admins, its creator and the people added; org: everyone in the organization"
    },
    "access": {
      "type": "object",
      "properties": {
        "role": {
          "type": "string",
          "enum": [
            "admin",
            "developer",
            "viewer"
          ],
          "description": "The caller's role on it"
        },
        "outside": {
          "type": "boolean",
          "description": "Shared with the caller from another organization"
        },
        "isCreator": {
          "type": "boolean",
          "description": "The caller created it"
        }
      },
      "required": [
        "role",
        "outside",
        "isCreator"
      ],
      "additionalProperties": false
    },
    "appCount": {
      "type": "integer"
    },
    "createdBy": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "userId": {
              "type": "string",
              "description": "User id, as members.list and the sharing calls use it"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "email": {
              "type": "string"
            }
          },
          "required": [
            "userId",
            "name",
            "email"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    }
  },
  "required": [
    "id",
    "name",
    "org",
    "visibility",
    "access",
    "appCount",
    "createdBy",
    "createdAt"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `update_project`

Rename a project, or change who in its organization can see it. Visibility is for its creator or an admin: private is admins, its creator and the people added; org is everyone in the organization

**Group:** `projects` · **Behavior:** Write

**Compact tool:** `projects_write` · **Action:** `update_project`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation         | REST                          | Required scopes  |
| ----------------- | ----------------------------- | ---------------- |
| `projects.update` | `PATCH /projects/{projectId}` | `projects:write` |

### Arguments

| Argument     | Type                | Required | Description                                                                          |
| ------------ | ------------------- | -------- | ------------------------------------------------------------------------------------ |
| `name`       | `string`            | No       | New project name                                                                     |
| `visibility` | `"private" / "org"` | No       | private: admins, its creator and the people added; org: everyone in the organization |
| `projectId`  | `string`            | Yes      | Project id                                                                           |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "New project name"
    },
    "visibility": {
      "type": "string",
      "enum": [
        "private",
        "org"
      ],
      "description": "private: admins, its creator and the people added; org: everyone in the organization"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    }
  },
  "required": [
    "projectId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Project id
* `name` (`string`): See the complete response schema below.
* `org` (`object`): The organization that owns it
* `visibility` (`"private" / "org"`): private: admins, its creator and the people added; org: everyone in the organization
* `access` (`object`): See the complete response schema below.
* `appCount` (`integer`): See the complete response schema below.
* `createdBy` (`object or null`): Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read
* `createdAt` (`string`): ISO 8601 timestamp (UTC)

<details>

<summary>Response schema: projects.update</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Project id"
    },
    "name": {
      "type": "string"
    },
    "org": {
      "type": "object",
      "properties": {
        "slug": {
          "type": "string"
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "slug",
        "name"
      ],
      "additionalProperties": false,
      "description": "The organization that owns it"
    },
    "visibility": {
      "type": "string",
      "enum": [
        "private",
        "org"
      ],
      "description": "private: admins, its creator and the people added; org: everyone in the organization"
    },
    "access": {
      "type": "object",
      "properties": {
        "role": {
          "type": "string",
          "enum": [
            "admin",
            "developer",
            "viewer"
          ],
          "description": "The caller's role on it"
        },
        "outside": {
          "type": "boolean",
          "description": "Shared with the caller from another organization"
        },
        "isCreator": {
          "type": "boolean",
          "description": "The caller created it"
        }
      },
      "required": [
        "role",
        "outside",
        "isCreator"
      ],
      "additionalProperties": false
    },
    "appCount": {
      "type": "integer"
    },
    "createdBy": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "userId": {
              "type": "string",
              "description": "User id, as members.list and the sharing calls use it"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "email": {
              "type": "string"
            }
          },
          "required": [
            "userId",
            "name",
            "email"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    }
  },
  "required": [
    "id",
    "name",
    "org",
    "visibility",
    "access",
    "appCount",
    "createdBy",
    "createdAt"
  ],
  "additionalProperties": false
}
```

</details>

## `delete_project`

Delete a project and every app in it. Admins may, and so may its creator when they created every app in it. Fails with project\_has\_running\_apps while any app is live. Irreversible.

**Group:** `projects` · **Behavior:** Write

**Compact tool:** `projects_write` · **Action:** `delete_project`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

Requires `confirm: true` for this irreversible action.

### Permissions and REST equivalents

| Operation         | REST                           | Required scopes  |
| ----------------- | ------------------------------ | ---------------- |
| `projects.delete` | `DELETE /projects/{projectId}` | `projects:admin` |

### Arguments

| Argument    | Type     | Required | Description                                |
| ----------- | -------- | -------- | ------------------------------------------ |
| `projectId` | `string` | Yes      | Project id                                 |
| `confirm`   | `true`   | Yes      | Must be true: this action cannot be undone |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "confirm": {
      "type": "boolean",
      "const": true,
      "description": "Must be true: this action cannot be undone"
    }
  },
  "required": [
    "projectId",
    "confirm"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Id of the deleted project
* `deleted` (`true`): See the complete response schema below.

<details>

<summary>Response schema: projects.delete</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Id of the deleted project"
    },
    "deleted": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "id",
    "deleted"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 project_has_running_apps`.


# Apps and templates

Discover, create and update container applications and ready-made templates.

Discover, create and update container applications and ready-made templates.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

Serverless capacity is CPU-only. GPU configurations require `config.grids.ids`; GPU templates carry this requirement in `notes`. Configure credentials in the portal when using the OpenAI profile. Null environment values preserve existing variables on update, including values hidden by that profile.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `list_apps`

List the apps the caller can see, oldest first. Across every project, or within one

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `list_apps`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation   | REST        | Required scopes |
| ----------- | ----------- | --------------- |
| `apps.list` | `GET /apps` | `apps:read`     |

### Arguments

| Argument    | Type      | Required | Description                                                                          |
| ----------- | --------- | -------- | ------------------------------------------------------------------------------------ |
| `projectId` | `string`  | No       | Limit the list to one project                                                        |
| `limit`     | `integer` | No       | Items per page, 1 to 200 (default 50)                                                |
| `cursor`    | `string`  | No       | nextCursor from the previous page; omit for the first page                           |
| `org`       | `string`  | No       | Slug of an organization (see orgs.list) to list only what is in it or shared into it |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Limit the list to one project"
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 200,
      "description": "Items per page, 1 to 200 (default 50)"
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "nextCursor from the previous page; omit for the first page"
    },
    "org": {
      "description": "Slug of an organization (see orgs.list) to list only what is in it or shared into it",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `apps` (`array<object>`): See the complete response schema below.
* `nextCursor` (`string or null`): Pass as cursor to fetch the next page; null on the last page

<details>

<summary>Response schema: apps.list</summary>

```json
{
  "type": "object",
  "properties": {
    "apps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "App id"
          },
          "projectId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "guid": {
            "type": "string",
            "description": "Runtime identifier of the app"
          },
          "source": {
            "description": "Where the image comes from: Docker Hub, a registry link, GitHub or an upload",
            "type": [
              "string",
              "null"
            ]
          },
          "image": {
            "type": [
              "string",
              "null"
            ]
          },
          "domain": {
            "description": "Hostname the app answers on: the default custom domain, else the Kinesis-issued one",
            "type": [
              "string",
              "null"
            ]
          },
          "buildState": {
            "description": "Build pipeline state for GitHub-sourced apps",
            "type": [
              "string",
              "null"
            ]
          },
          "access": {
            "type": "object",
            "properties": {
              "role": {
                "type": "string",
                "enum": [
                  "admin",
                  "developer",
                  "viewer"
                ],
                "description": "The caller's role on it"
              },
              "outside": {
                "type": "boolean",
                "description": "Shared with the caller from another organization"
              },
              "isCreator": {
                "type": "boolean",
                "description": "The caller created it"
              }
            },
            "required": [
              "role",
              "outside",
              "isCreator"
            ],
            "additionalProperties": false
          },
          "createdBy": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string",
                    "description": "User id, as members.list and the sharing calls use it"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "email": {
                    "type": "string"
                  }
                },
                "required": [
                  "userId",
                  "name",
                  "email"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          }
        },
        "required": [
          "id",
          "projectId",
          "name",
          "guid",
          "source",
          "image",
          "domain",
          "buildState",
          "access",
          "createdBy",
          "createdAt"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": {
      "description": "Pass as cursor to fetch the next page; null on the last page",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "apps",
    "nextCursor"
  ],
  "additionalProperties": false
}
```

</details>

## `list_templates`

The gallery's app templates as ready-made create\_app configurations. Ollama chat with a chosen model, vLLM servers, ...: pass a template's config to create\_app with a name, edited as needed; notes says what a template needs filled in first.

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `list_templates`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation        | REST             | Required scopes                                                             |
| ---------------- | ---------------- | --------------------------------------------------------------------------- |
| `templates.list` | `GET /templates` | Authenticated connection; organization and resource permissions still apply |

### Arguments

| Argument   | Type                                            | Required | Description                                     |
| ---------- | ----------------------------------------------- | -------- | ----------------------------------------------- |
| `search`   | `string`                                        | No       | Case-insensitive substring of the name or image |
| `category` | `"llm_general" / "llm_performance" / "general"` | No       | Only templates of this category                 |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "search": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "Case-insensitive substring of the name or image"
    },
    "category": {
      "type": "string",
      "enum": [
        "llm_general",
        "llm_performance",
        "general"
      ],
      "description": "Only templates of this category"
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `templates` (`array<object>`): See the complete response schema below.
* `galleryUrl` (`string`): The dashboard gallery, where the same templates are deployed by hand

<details>

<summary>Response schema: templates.list</summary>

```json
{
  "type": "object",
  "properties": {
    "templates": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Template id"
          },
          "name": {
            "type": "string",
            "description": "Suggested app name"
          },
          "category": {
            "type": "string",
            "enum": [
              "llm_general",
              "llm_performance",
              "general"
            ]
          },
          "image": {
            "description": "Container image the template deploys",
            "type": [
              "string",
              "null"
            ]
          },
          "paidTierOnly": {
            "type": "boolean",
            "description": "Meant for organizations with a payment on record"
          },
          "notes": {
            "description": "What to fill in before deploying, when the template is not complete as is",
            "type": [
              "string",
              "null"
            ]
          },
          "config": {
            "type": "object",
            "properties": {
              "image": {
                "description": "Container image reference, e.g. nginx:1.27 or ghcr.io/org/app:tag",
                "type": "string",
                "minLength": 1,
                "maxLength": 512
              },
              "registryAuth": {
                "description": "Credentials for a private image",
                "type": "object",
                "properties": {
                  "username": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Registry login"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Registry password or token (write-only)"
                  }
                },
                "required": [
                  "username",
                  "password"
                ],
                "additionalProperties": false
              },
              "ports": {
                "description": "Ports to expose",
                "maxItems": 32,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "port": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 65535,
                      "description": "Container port"
                    },
                    "protocol": {
                      "type": "string",
                      "enum": [
                        "tcp",
                        "udp",
                        "tcp_load_balancer",
                        "tcps_load_balancer",
                        "http_load_balancer",
                        "https_load_balancer",
                        "http80_load_balancer",
                        "https443_load_balancer",
                        "grpcs_load_balancer"
                      ],
                      "description": "How the port is exposed"
                    }
                  },
                  "required": [
                    "port",
                    "protocol"
                  ],
                  "additionalProperties": false
                }
              },
              "env": {
                "description": "Environment variables",
                "maxItems": 200,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "string",
                      "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
                      "description": "Variable name"
                    },
                    "value": {
                      "description": "Value; null keeps the stored value of an existing variable",
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "secret": {
                      "description": "Store as a secret: never returned by reads",
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "key",
                    "value"
                  ],
                  "additionalProperties": false
                }
              },
              "command": {
                "description": "Container command (CMD) override",
                "maxItems": 256,
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "entrypoint": {
                "description": "Container entrypoint override",
                "maxItems": 256,
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "startupCommand": {
                "description": "Command run inside the container once it starts; null clears it",
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "args": {
                        "minItems": 1,
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "description": "Command and arguments"
                      },
                      "user": {
                        "description": "User to run as",
                        "type": "string"
                      },
                      "timeoutSec": {
                        "description": "Seconds before the command is considered failed",
                        "type": "integer",
                        "minimum": 0
                      },
                      "startPeriodSec": {
                        "description": "Seconds to wait after the container starts",
                        "type": "integer",
                        "minimum": 0
                      }
                    },
                    "required": [
                      "args"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "healthcheck": {
                "description": "Container health check; null clears it",
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "test": {
                        "minItems": 1,
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "description": "Command that must exit 0 for the app to count as healthy"
                      },
                      "intervalSec": {
                        "type": "integer",
                        "minimum": 1,
                        "description": "Seconds between checks"
                      }
                    },
                    "required": [
                      "test",
                      "intervalSec"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "restartPolicy": {
                "description": "Restart behaviour",
                "type": "object",
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "disabled",
                      "always",
                      "on_failure",
                      "task_completion"
                    ],
                    "description": "When to restart a container that exits"
                  },
                  "startTimeoutSec": {
                    "description": "Seconds a start may take before it counts as failed",
                    "type": "integer",
                    "minimum": 0
                  },
                  "maxRetriesPerNode": {
                    "description": "Restarts allowed on one server before moving on",
                    "type": "integer",
                    "minimum": 0
                  },
                  "maxErrorTolerance": {
                    "description": "Failures tolerated before the app is stopped",
                    "type": "integer",
                    "minimum": 0
                  },
                  "numTasks": {
                    "description": "For task_completion: runs to complete before stopping",
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "required": [
                  "mode"
                ],
                "additionalProperties": false
              },
              "instances": {
                "description": "Instance count range",
                "type": "object",
                "properties": {
                  "min": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Instances to keep running"
                  },
                  "max": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Upper bound for scaling"
                  }
                },
                "required": [
                  "min",
                  "max"
                ],
                "additionalProperties": false
              },
              "stateful": {
                "description": "Keep each instance pinned to its server and disk",
                "type": "boolean"
              },
              "hardware": {
                "description": "Hardware requirements per instance",
                "type": "object",
                "properties": {
                  "gpuCount": {
                    "description": "GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only",
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 16
                  },
                  "gpuModels": {
                    "description": "Acceptable GPU models, e.g. H100; empty means any",
                    "maxItems": 16,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1
                    }
                  },
                  "gpuManufacturer": {
                    "description": "GPU vendor; null means any",
                    "anyOf": [
                      {
                        "type": "string",
                        "enum": [
                          "amd",
                          "apple",
                          "intel",
                          "nvidia",
                          "xilinx"
                        ]
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "gpuVramBytes": {
                    "description": "Minimum VRAM per GPU, in bytes",
                    "type": "integer",
                    "minimum": 0
                  },
                  "ramBytes": {
                    "description": "RAM per instance, in bytes",
                    "type": "integer",
                    "exclusiveMinimum": 0
                  },
                  "storageBytes": {
                    "description": "Disk per instance, in bytes",
                    "type": "integer",
                    "exclusiveMinimum": 0
                  },
                  "shmemBytes": {
                    "description": "Shared memory (/dev/shm) size, in bytes",
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "additionalProperties": false
              },
              "placement": {
                "description": "Where the app may run and who may reach it",
                "type": "object",
                "properties": {
                  "regions": {
                    "description": "Kinesis regions to run in; empty means any",
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "us-west",
                        "us-east",
                        "us-central",
                        "ca-west",
                        "ca-east",
                        "ca-central",
                        "eu-west",
                        "eu-north",
                        "eu-east",
                        "eu-south",
                        "eu-central",
                        "eu-south-east",
                        "ap-east",
                        "ap-south-east",
                        "ap-north-east",
                        "ap-south"
                      ]
                    }
                  },
                  "providers": {
                    "description": "Restrict placement to specific providers and regions",
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "csp": {
                          "type": "string",
                          "enum": [
                            "aws",
                            "azure",
                            "gcp",
                            "kinesis"
                          ],
                          "description": "Cloud provider"
                        },
                        "regions": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "region": {
                                "type": "string",
                                "minLength": 1,
                                "description": "Provider region"
                              },
                              "zones": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                },
                                "description": "Zones within the region; empty means any"
                              }
                            },
                            "required": [
                              "region",
                              "zones"
                            ],
                            "additionalProperties": false
                          },
                          "description": "Regions of the provider to allow"
                        }
                      },
                      "required": [
                        "csp",
                        "regions"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "allowedIps": {
                    "description": "Source IPs or CIDR ranges allowed to reach the app; empty means everyone",
                    "maxItems": 100,
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "additionalProperties": false
              },
              "grids": {
                "description": "Run on grids instead of serverless capacity (members only, on grids they may deploy to)",
                "type": "object",
                "properties": {
                  "ids": {
                    "description": "Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one",
                    "maxItems": 20,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "minLength": 1
                    }
                  },
                  "priorities": {
                    "description": "Priority per grid id, -100 to 100",
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {
                      "type": "integer",
                      "minimum": -100,
                      "maximum": 100
                    }
                  }
                },
                "additionalProperties": false
              },
              "volumes": {
                "description": "Persistent volumes",
                "maxItems": 4,
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$",
                      "description": "Volume name, unique within the app"
                    },
                    "target": {
                      "type": "string",
                      "pattern": "^\\/.+",
                      "description": "Absolute mount path inside the container"
                    },
                    "declaredSizeBytes": {
                      "description": "Expected size, used for placement; 0 or omitted means unspecified",
                      "type": "integer",
                      "minimum": 0,
                      "maximum": 17592186044416
                    }
                  },
                  "required": [
                    "name",
                    "target"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "additionalProperties": false,
            "description": "Pass as config to apps.create, edited as needed"
          }
        },
        "required": [
          "id",
          "name",
          "category",
          "image",
          "paidTierOnly",
          "notes",
          "config"
        ],
        "additionalProperties": false
      }
    },
    "galleryUrl": {
      "type": "string",
      "description": "The dashboard gallery, where the same templates are deployed by hand"
    }
  },
  "required": [
    "templates",
    "galleryUrl"
  ],
  "additionalProperties": false
}
```

</details>

## `get_app`

Get an app with its full configuration and last known status

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `get_app`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation  | REST                                     | Required scopes |
| ---------- | ---------------------------------------- | --------------- |
| `apps.get` | `GET /projects/{projectId}/apps/{appId}` | `apps:read`     |

### Arguments

| Argument    | Type     | Required | Description |
| ----------- | -------- | -------- | ----------- |
| `projectId` | `string` | Yes      | Project id  |
| `appId`     | `string` | Yes      | App id      |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): App id
* `projectId` (`string`): See the complete response schema below.
* `name` (`string`): See the complete response schema below.
* `guid` (`string`): Runtime identifier of the app
* `source` (`string or null`): Where the image comes from: Docker Hub, a registry link, GitHub or an upload
* `image` (`string or null`): See the complete response schema below.
* `domain` (`string or null`): Hostname the app answers on: the default custom domain, else the Kinesis-issued one
* `buildState` (`string or null`): Build pipeline state for GitHub-sourced apps
* `access` (`object`): See the complete response schema below.
* `createdBy` (`object or null`): Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read
* `createdAt` (`string`): ISO 8601 timestamp (UTC)
* `status` (`object`): Status as last cached; use the status endpoint for a live read
* `config` (`object or null`): See the complete response schema below.
* `github` (`object or null`): Repository settings of a GitHub-sourced app
* `defaultDomain` (`string or null`): Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one

<details>

<summary>Response schema: apps.get</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "App id"
    },
    "projectId": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "guid": {
      "type": "string",
      "description": "Runtime identifier of the app"
    },
    "source": {
      "description": "Where the image comes from: Docker Hub, a registry link, GitHub or an upload",
      "type": [
        "string",
        "null"
      ]
    },
    "image": {
      "type": [
        "string",
        "null"
      ]
    },
    "domain": {
      "description": "Hostname the app answers on: the default custom domain, else the Kinesis-issued one",
      "type": [
        "string",
        "null"
      ]
    },
    "buildState": {
      "description": "Build pipeline state for GitHub-sourced apps",
      "type": [
        "string",
        "null"
      ]
    },
    "access": {
      "type": "object",
      "properties": {
        "role": {
          "type": "string",
          "enum": [
            "admin",
            "developer",
            "viewer"
          ],
          "description": "The caller's role on it"
        },
        "outside": {
          "type": "boolean",
          "description": "Shared with the caller from another organization"
        },
        "isCreator": {
          "type": "boolean",
          "description": "The caller created it"
        }
      },
      "required": [
        "role",
        "outside",
        "isCreator"
      ],
      "additionalProperties": false
    },
    "createdBy": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "userId": {
              "type": "string",
              "description": "User id, as members.list and the sharing calls use it"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "email": {
              "type": "string"
            }
          },
          "required": [
            "userId",
            "name",
            "email"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    },
    "status": {
      "type": "object",
      "properties": {
        "state": {
          "type": "string",
          "enum": [
            "unknown",
            "running",
            "stopped",
            "starting",
            "stopping",
            "scaling",
            "updating",
            "downloading",
            "provisioning",
            "start_blocked",
            "error",
            "building",
            "build_failed"
          ],
          "description": "Aggregate state; build states win while there is no runnable image"
        },
        "targetState": {
          "type": "string",
          "enum": [
            "running",
            "stopped",
            "unknown"
          ],
          "description": "What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"
        },
        "nodes": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "address": {
                "type": "string"
              },
              "state": {
                "type": "string"
              },
              "reportedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "When the server last reported this state; not a start time"
              }
            },
            "required": [
              "address",
              "state",
              "reportedAt"
            ],
            "additionalProperties": false
          },
          "description": "Servers the app is placed on and the container state on each"
        },
        "endpoints": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "port": {
                "type": "integer",
                "description": "Container port, as in config.ports"
              },
              "protocol": {
                "type": "string",
                "description": "Protocol of that port, as in config.ports"
              },
              "address": {
                "description": "An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired",
                "type": [
                  "string",
                  "null"
                ]
              },
              "server": {
                "description": "Server address of a direct tcp or udp endpoint; null when load-balanced",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "port",
              "protocol",
              "address",
              "server"
            ],
            "additionalProperties": false
          },
          "description": "Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"
        },
        "errors": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "at": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "details": {
                "anyOf": [
                  {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {}
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Extra data the runtime attached, such as the grid or server involved"
              }
            },
            "required": [
              "type",
              "message",
              "at",
              "details"
            ],
            "additionalProperties": false
          },
          "description": "Recent runtime errors, newest first"
        },
        "workOrders": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "description": "What the runtime asked the server to do"
              },
              "server": {
                "type": "string",
                "description": "Server address"
              },
              "status": {
                "type": "string"
              },
              "createdAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "startedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "completedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "type",
              "server",
              "status",
              "createdAt",
              "startedAt",
              "completedAt"
            ],
            "additionalProperties": false
          },
          "description": "The runtime's latest instructions to servers, newest first; an order that never completes is the stall"
        },
        "grid": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "fill": {
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "target": {
                          "type": "integer",
                          "description": "Members the grid is composed to have"
                        },
                        "online": {
                          "type": "integer",
                          "description": "Members the app runs on"
                        },
                        "pending": {
                          "type": "integer"
                        },
                        "converging": {
                          "type": "boolean",
                          "description": "The grid is still acquiring members or placing the app"
                        }
                      },
                      "required": [
                        "target",
                        "online",
                        "pending",
                        "converging"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "How far the grid has filled for this app"
                },
                "idleReclaim": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "gridId": {
                        "type": "string"
                      },
                      "warnedAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "evictAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "When the app becomes evictable; null when the grid only warns"
                      },
                      "evictedAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "cooldownEndsAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "When the grid takes the app again after an eviction"
                      },
                      "evidence": {
                        "type": "string",
                        "description": "Why the grid considers the app idle"
                      }
                    },
                    "required": [
                      "gridId",
                      "warnedAt",
                      "evictAt",
                      "evictedAt",
                      "cooldownEndsAt",
                      "evidence"
                    ],
                    "additionalProperties": false
                  },
                  "description": "Grids whose idle policy has warned or evicted the app"
                },
                "queue": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "gridId": {
                        "type": "string"
                      },
                      "position": {
                        "type": "integer",
                        "description": "1-based place in the grid's fair-share queue"
                      },
                      "queueLength": {
                        "type": "integer"
                      },
                      "enqueuedAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "held": {
                        "type": "boolean",
                        "description": "The scheduler keeps the app off the grid until its turn comes"
                      }
                    },
                    "required": [
                      "gridId",
                      "position",
                      "queueLength",
                      "enqueuedAt",
                      "held"
                    ],
                    "additionalProperties": false
                  },
                  "description": "Full grids the app is queued on"
                }
              },
              "required": [
                "fill",
                "idleReclaim",
                "queue"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "Grid placement details; null for a serverless app"
        },
        "pollAfterSec": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "description": "Seconds to wait before reading the status again while it is changing; null in a settled state"
        }
      },
      "required": [
        "state",
        "targetState",
        "nodes",
        "endpoints",
        "errors",
        "workOrders",
        "grid",
        "pollAfterSec"
      ],
      "additionalProperties": false,
      "description": "Status as last cached; use the status endpoint for a live read"
    },
    "config": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "image": {
              "type": "string",
              "description": "Container image reference"
            },
            "imageTag": {
              "description": "Tag, kept separately for Docker Hub images",
              "type": [
                "string",
                "null"
              ]
            },
            "registryUsername": {
              "description": "Registry login; the password is write-only",
              "type": [
                "string",
                "null"
              ]
            },
            "ports": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "port": {
                    "type": "integer"
                  },
                  "protocol": {
                    "type": "string"
                  }
                },
                "required": [
                  "port",
                  "protocol"
                ],
                "additionalProperties": false
              }
            },
            "env": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string"
                  },
                  "value": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "secret": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "key",
                  "value",
                  "secret"
                ],
                "additionalProperties": false
              },
              "description": "Environment variables; value is null for secrets, and on a connection that takes no credentials for values that look like one (null keeps a value on update)"
            },
            "command": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "entrypoint": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "startupCommand": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "args": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "user": {
                      "type": "string"
                    },
                    "timeoutSec": {
                      "type": "number"
                    },
                    "startPeriodSec": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "args",
                    "user",
                    "timeoutSec",
                    "startPeriodSec"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "healthcheck": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "test": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "intervalSec": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "test",
                    "intervalSec"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "restartPolicy": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "mode": {
                      "type": "string"
                    },
                    "startTimeoutSec": {
                      "type": "number"
                    },
                    "maxRetriesPerNode": {
                      "type": "number"
                    },
                    "maxErrorTolerance": {
                      "type": "number"
                    },
                    "numTasks": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "mode",
                    "startTimeoutSec",
                    "maxRetriesPerNode",
                    "maxErrorTolerance",
                    "numTasks"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "instances": {
              "type": "object",
              "properties": {
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                }
              },
              "required": [
                "min",
                "max"
              ],
              "additionalProperties": false
            },
            "stateful": {
              "type": "boolean"
            },
            "hardware": {
              "type": "object",
              "properties": {
                "gpuCount": {
                  "type": "integer"
                },
                "gpuModels": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "gpuManufacturer": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "gpuVramBytes": {
                  "type": "number"
                },
                "ramBytes": {
                  "type": "number"
                },
                "storageBytes": {
                  "type": "number"
                },
                "shmemBytes": {
                  "type": "number"
                }
              },
              "required": [
                "gpuCount",
                "gpuModels",
                "gpuManufacturer",
                "gpuVramBytes",
                "ramBytes",
                "storageBytes",
                "shmemBytes"
              ],
              "additionalProperties": false
            },
            "placement": {
              "type": "object",
              "properties": {
                "regions": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Kinesis regions, e.g. us-west"
                },
                "providers": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "csp": {
                        "type": "string"
                      },
                      "regions": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "region": {
                              "type": "string"
                            },
                            "zones": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          },
                          "required": [
                            "region",
                            "zones"
                          ],
                          "additionalProperties": false
                        }
                      }
                    },
                    "required": [
                      "csp",
                      "regions"
                    ],
                    "additionalProperties": false
                  }
                },
                "allowedIps": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Source IPs or CIDR ranges allowed to reach the app"
                }
              },
              "required": [
                "regions",
                "providers",
                "allowedIps"
              ],
              "additionalProperties": false
            },
            "grids": {
              "type": "object",
              "properties": {
                "ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "priorities": {
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {
                    "type": "number"
                  },
                  "description": "Priority per grid id"
                }
              },
              "required": [
                "ids",
                "priorities"
              ],
              "additionalProperties": false
            },
            "volumes": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "target": {
                    "type": "string"
                  },
                  "declaredSizeBytes": {
                    "type": "number"
                  }
                },
                "required": [
                  "name",
                  "target",
                  "declaredSizeBytes"
                ],
                "additionalProperties": false
              }
            },
            "domains": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Custom domains attached to the app (see the domain operations)"
            }
          },
          "required": [
            "image",
            "imageTag",
            "registryUsername",
            "ports",
            "env",
            "command",
            "entrypoint",
            "startupCommand",
            "healthcheck",
            "restartPolicy",
            "instances",
            "stateful",
            "hardware",
            "placement",
            "grids",
            "volumes",
            "domains"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ]
    },
    "github": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "installationId": {
              "type": "integer",
              "description": "GitHub App installation the repository is read through"
            },
            "repo": {
              "type": "string",
              "description": "owner/name"
            },
            "url": {
              "type": "string"
            },
            "branch": {
              "type": "string",
              "description": "Branch that is built and, with autoDeploy, followed"
            },
            "contextDir": {
              "type": "string"
            },
            "dockerfilePath": {
              "description": "Dockerfile path relative to contextDir; not used while dockerfileContent is set",
              "type": [
                "string",
                "null"
              ]
            },
            "dockerfileOverride": {
              "type": "boolean",
              "description": "An inline Dockerfile (dockerfileContent) is built instead of dockerfilePath"
            },
            "dockerfileContent": {
              "description": "The inline Dockerfile that is built; null when the repository's own file is used",
              "type": [
                "string",
                "null"
              ]
            },
            "autoDeploy": {
              "type": "boolean",
              "description": "Every push to the branch rebuilds and redeploys"
            },
            "clonedFrom": {
              "description": "Public repository (owner/name) the app's repository was copied from at creation; null otherwise",
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "installationId",
            "repo",
            "url",
            "branch",
            "contextDir",
            "dockerfilePath",
            "dockerfileOverride",
            "dockerfileContent",
            "autoDeploy",
            "clonedFrom"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Repository settings of a GitHub-sourced app"
    },
    "defaultDomain": {
      "description": "Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "id",
    "projectId",
    "name",
    "guid",
    "source",
    "image",
    "domain",
    "buildState",
    "access",
    "createdBy",
    "createdAt",
    "status",
    "config",
    "github",
    "defaultDomain"
  ],
  "additionalProperties": false
}
```

</details>

## `check_placement`

See whether any server could run an app or a configuration change. Reports servers with capacity now, hibernated ones that would fit once woken, whether one can be provisioned on demand, and why sampled servers were rejected, before creating, updating or starting. With appId the app's stored configuration is checked, config (placement-relevant fields only) layered on top; without appId, config describes an app to be created in the project.

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `check_placement`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation            | REST                                                | Required scopes |
| -------------------- | --------------------------------------------------- | --------------- |
| `apps.placement`     | `POST /projects/{projectId}/apps/{appId}/placement` | `apps:read`     |
| `projects.placement` | `POST /projects/{projectId}/placement`              | `apps:read`     |

### Arguments

| Argument    | Type     | Required | Description                                                                                                    |
| ----------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `projectId` | `string` | Yes      | Project id                                                                                                     |
| `appId`     | `string` | No       | App id                                                                                                         |
| `config`    | `object` | No       | The placement-relevant parts of an app configuration: instances, stateful, hardware, placement, grids, volumes |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "config": {
      "type": "object",
      "properties": {
        "instances": {
          "type": "object",
          "properties": {
            "min": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Instances to keep running"
            },
            "max": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Upper bound for scaling"
            }
          },
          "required": [
            "min",
            "max"
          ],
          "additionalProperties": false,
          "description": "Instance count range"
        },
        "stateful": {
          "type": "boolean",
          "description": "Keep each instance pinned to its server and disk"
        },
        "hardware": {
          "type": "object",
          "properties": {
            "gpuCount": {
              "type": "integer",
              "minimum": 0,
              "maximum": 16,
              "description": "GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only"
            },
            "gpuModels": {
              "maxItems": 16,
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "description": "Acceptable GPU models, e.g. H100; empty means any"
            },
            "gpuManufacturer": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "amd",
                    "apple",
                    "intel",
                    "nvidia",
                    "xilinx"
                  ]
                },
                {
                  "type": "null"
                }
              ],
              "description": "GPU vendor; null means any"
            },
            "gpuVramBytes": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Minimum VRAM per GPU, in bytes"
            },
            "ramBytes": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "RAM per instance, in bytes"
            },
            "storageBytes": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "Disk per instance, in bytes"
            },
            "shmemBytes": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Shared memory (/dev/shm) size, in bytes"
            }
          },
          "additionalProperties": false,
          "description": "Hardware requirements per instance"
        },
        "placement": {
          "type": "object",
          "properties": {
            "regions": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "us-west",
                  "us-east",
                  "us-central",
                  "ca-west",
                  "ca-east",
                  "ca-central",
                  "eu-west",
                  "eu-north",
                  "eu-east",
                  "eu-south",
                  "eu-central",
                  "eu-south-east",
                  "ap-east",
                  "ap-south-east",
                  "ap-north-east",
                  "ap-south"
                ]
              },
              "description": "Kinesis regions to run in; empty means any"
            },
            "providers": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "csp": {
                    "type": "string",
                    "enum": [
                      "aws",
                      "azure",
                      "gcp",
                      "kinesis"
                    ],
                    "description": "Cloud provider"
                  },
                  "regions": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "region": {
                          "type": "string",
                          "minLength": 1,
                          "description": "Provider region"
                        },
                        "zones": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Zones within the region; empty means any"
                        }
                      },
                      "required": [
                        "region",
                        "zones"
                      ],
                      "additionalProperties": false
                    },
                    "description": "Regions of the provider to allow"
                  }
                },
                "required": [
                  "csp",
                  "regions"
                ],
                "additionalProperties": false
              },
              "description": "Restrict placement to specific providers and regions"
            },
            "allowedIps": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Source IPs or CIDR ranges allowed to reach the app; empty means everyone"
            }
          },
          "additionalProperties": false,
          "description": "Where the app may run and who may reach it"
        },
        "grids": {
          "type": "object",
          "properties": {
            "ids": {
              "maxItems": 20,
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "description": "Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one"
            },
            "priorities": {
              "propertyNames": {
                "type": "string"
              },
              "description": "Priority per grid id, -100 to 100",
              "type": "object",
              "properties": {},
              "additionalProperties": {
                "type": "integer",
                "minimum": -100,
                "maximum": 100
              }
            }
          },
          "additionalProperties": false,
          "description": "Run on grids instead of serverless capacity (members only, on grids they may deploy to)"
        },
        "volumes": {
          "maxItems": 4,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$",
                "description": "Volume name, unique within the app"
              },
              "target": {
                "type": "string",
                "pattern": "^\\/.+",
                "description": "Absolute mount path inside the container"
              },
              "declaredSizeBytes": {
                "type": "integer",
                "minimum": 0,
                "maximum": 17592186044416,
                "description": "Expected size, used for placement; 0 or omitted means unspecified"
              }
            },
            "required": [
              "name",
              "target"
            ],
            "additionalProperties": false
          },
          "description": "Persistent volumes"
        }
      },
      "additionalProperties": false,
      "description": "The placement-relevant parts of an app configuration: instances, stateful, hardware, placement, grids, volumes"
    }
  },
  "required": [
    "projectId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

**`apps.placement`**

* `placeable` (`boolean`): A server can take the app now, a hibernated one would fit once woken, or one can be provisioned
* `serversWithCapacity` (`integer`): Servers that match and have room now
* `serversWithoutCapacity` (`integer`): Servers that match but are full
* `wakeable` (`integer`): Hibernated servers that would match once woken (power\_servers wake, or the automatic wake-up on start)
* `wakeableAwaitingStock` (`integer`): Of wakeable, servers whose provider has no stock right now, so a wake would not land yet
* `provisionable` (`boolean`): The platform can provision a matching cloud machine on demand for a serverless app
* `rejections` (`array<object>`): Why sampled servers cannot take the app; each distinct reason appears first, then repeats

<details>

<summary>Response schema: apps.placement</summary>

```json
{
  "type": "object",
  "properties": {
    "placeable": {
      "type": "boolean",
      "description": "A server can take the app now, a hibernated one would fit once woken, or one can be provisioned"
    },
    "serversWithCapacity": {
      "type": "integer",
      "description": "Servers that match and have room now"
    },
    "serversWithoutCapacity": {
      "type": "integer",
      "description": "Servers that match but are full"
    },
    "wakeable": {
      "type": "integer",
      "description": "Hibernated servers that would match once woken (power_servers wake, or the automatic wake-up on start)"
    },
    "wakeableAwaitingStock": {
      "type": "integer",
      "description": "Of wakeable, servers whose provider has no stock right now, so a wake would not land yet"
    },
    "provisionable": {
      "type": "boolean",
      "description": "The platform can provision a matching cloud machine on demand for a serverless app"
    },
    "rejections": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "server": {
            "type": "string",
            "description": "Server address"
          },
          "reason": {
            "type": "string"
          }
        },
        "required": [
          "server",
          "reason"
        ],
        "additionalProperties": false
      },
      "description": "Why sampled servers cannot take the app; each distinct reason appears first, then repeats"
    }
  },
  "required": [
    "placeable",
    "serversWithCapacity",
    "serversWithoutCapacity",
    "wakeable",
    "wakeableAwaitingStock",
    "provisionable",
    "rejections"
  ],
  "additionalProperties": false
}
```

</details>

\*\*\`projects.placement\`\*\*

* `placeable` (`boolean`): A server can take the app now, a hibernated one would fit once woken, or one can be provisioned
* `serversWithCapacity` (`integer`): Servers that match and have room now
* `serversWithoutCapacity` (`integer`): Servers that match but are full
* `wakeable` (`integer`): Hibernated servers that would match once woken (power\_servers wake, or the automatic wake-up on start)
* `wakeableAwaitingStock` (`integer`): Of wakeable, servers whose provider has no stock right now, so a wake would not land yet
* `provisionable` (`boolean`): The platform can provision a matching cloud machine on demand for a serverless app
* `rejections` (`array<object>`): Why sampled servers cannot take the app; each distinct reason appears first, then repeats

<details>

<summary>Response schema: projects.placement</summary>

```json
{
  "type": "object",
  "properties": {
    "placeable": {
      "type": "boolean",
      "description": "A server can take the app now, a hibernated one would fit once woken, or one can be provisioned"
    },
    "serversWithCapacity": {
      "type": "integer",
      "description": "Servers that match and have room now"
    },
    "serversWithoutCapacity": {
      "type": "integer",
      "description": "Servers that match but are full"
    },
    "wakeable": {
      "type": "integer",
      "description": "Hibernated servers that would match once woken (power_servers wake, or the automatic wake-up on start)"
    },
    "wakeableAwaitingStock": {
      "type": "integer",
      "description": "Of wakeable, servers whose provider has no stock right now, so a wake would not land yet"
    },
    "provisionable": {
      "type": "boolean",
      "description": "The platform can provision a matching cloud machine on demand for a serverless app"
    },
    "rejections": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "server": {
            "type": "string",
            "description": "Server address"
          },
          "reason": {
            "type": "string"
          }
        },
        "required": [
          "server",
          "reason"
        ],
        "additionalProperties": false
      },
      "description": "Why sampled servers cannot take the app; each distinct reason appears first, then repeats"
    }
  },
  "required": [
    "placeable",
    "serversWithCapacity",
    "serversWithoutCapacity",
    "wakeable",
    "wakeableAwaitingStock",
    "provisionable",
    "rejections"
  ],
  "additionalProperties": false
}
```

</details>

## `create_app`

Create an app from an image or a GitHub repository and start it. config.image pulls an image; source builds a connected repository (one without a Dockerfile takes an inline source.dockerfileContent); the start begins spending credits. envFrom copies another app's environment variables, secret values included, on the server. Omitted config fields take the dashboard defaults (1 GiB RAM, 1 GB disk, 1 to 3 instances, no GPU; a GPU app needs config.grids.ids). Returns the app and how the automatic start went; call get\_app\_status to follow it, or get\_build with buildId for a repository app. read\_docs(deploy) lists every field.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `create_app`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: true`

### Permissions and REST equivalents

| Operation     | REST                              | Required scopes               |
| ------------- | --------------------------------- | ----------------------------- |
| `apps.create` | `POST /projects/{projectId}/apps` | `apps:write`, `compute:spend` |

### Arguments

| Argument         | Type     | Required | Description                                                                                                                                                           |
| ---------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`           | `string` | Yes      | App name                                                                                                                                                              |
| `config`         | `object` | Yes      | Initial configuration; image is required unless source is set                                                                                                         |
| `source`         | `object` | No       | Build the image from a GitHub repository the caller connected (members of the project's organization); omit to pull config.image                                      |
| `envFrom`        | `object` | No       | Copy another app's environment variables, secret values included, on the server (developer access to that app); use overrides instead of config.env                   |
| `projectId`      | `string` | Yes      | Project to create the app in                                                                                                                                          |
| `idempotencyKey` | `string` | No       | Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "App name"
    },
    "config": {
      "type": "object",
      "properties": {
        "image": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Container image reference, e.g. nginx:1.27 or ghcr.io/org/app:tag"
        },
        "registryAuth": {
          "type": "object",
          "properties": {
            "username": {
              "type": "string",
              "minLength": 1,
              "description": "Registry login"
            },
            "password": {
              "type": "string",
              "minLength": 1,
              "description": "Registry password or token (write-only)"
            }
          },
          "required": [
            "username",
            "password"
          ],
          "additionalProperties": false,
          "description": "Credentials for a private image"
        },
        "ports": {
          "maxItems": 32,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "port": {
                "type": "integer",
                "minimum": 1,
                "maximum": 65535,
                "description": "Container port"
              },
              "protocol": {
                "type": "string",
                "enum": [
                  "tcp",
                  "udp",
                  "tcp_load_balancer",
                  "tcps_load_balancer",
                  "http_load_balancer",
                  "https_load_balancer",
                  "http80_load_balancer",
                  "https443_load_balancer",
                  "grpcs_load_balancer"
                ],
                "description": "How the port is exposed"
              }
            },
            "required": [
              "port",
              "protocol"
            ],
            "additionalProperties": {}
          },
          "description": "Ports to expose"
        },
        "env": {
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
                "description": "Variable name"
              },
              "value": {
                "description": "Value; null keeps the stored value of an existing variable",
                "type": [
                  "string",
                  "null"
                ]
              },
              "secret": {
                "type": "boolean",
                "description": "Store as a secret: never returned by reads"
              }
            },
            "required": [
              "key",
              "value"
            ],
            "additionalProperties": {}
          },
          "description": "Environment variables"
        },
        "command": {
          "maxItems": 256,
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Container command (CMD) override"
        },
        "entrypoint": {
          "maxItems": 256,
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Container entrypoint override"
        },
        "startupCommand": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "args": {
                  "minItems": 1,
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Command and arguments"
                },
                "user": {
                  "type": "string",
                  "description": "User to run as"
                },
                "timeoutSec": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Seconds before the command is considered failed"
                },
                "startPeriodSec": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Seconds to wait after the container starts"
                }
              },
              "required": [
                "args"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "Command run inside the container once it starts; null clears it"
        },
        "healthcheck": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "test": {
                  "minItems": 1,
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Command that must exit 0 for the app to count as healthy"
                },
                "intervalSec": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 9007199254740991,
                  "description": "Seconds between checks"
                }
              },
              "required": [
                "test",
                "intervalSec"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "Container health check; null clears it"
        },
        "restartPolicy": {
          "type": "object",
          "properties": {
            "mode": {
              "type": "string",
              "enum": [
                "disabled",
                "always",
                "on_failure",
                "task_completion"
              ],
              "description": "When to restart a container that exits"
            },
            "startTimeoutSec": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Seconds a start may take before it counts as failed"
            },
            "maxRetriesPerNode": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Restarts allowed on one server before moving on"
            },
            "maxErrorTolerance": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Failures tolerated before the app is stopped"
            },
            "numTasks": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "For task_completion: runs to complete before stopping"
            }
          },
          "required": [
            "mode"
          ],
          "additionalProperties": false,
          "description": "Restart behaviour"
        },
        "instances": {
          "type": "object",
          "properties": {
            "min": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Instances to keep running"
            },
            "max": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Upper bound for scaling"
            }
          },
          "required": [
            "min",
            "max"
          ],
          "additionalProperties": false,
          "description": "Instance count range"
        },
        "stateful": {
          "type": "boolean",
          "description": "Keep each instance pinned to its server and disk"
        },
        "hardware": {
          "type": "object",
          "properties": {
            "gpuCount": {
              "type": "integer",
              "minimum": 0,
              "maximum": 16,
              "description": "GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only"
            },
            "gpuModels": {
              "maxItems": 16,
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "description": "Acceptable GPU models, e.g. H100; empty means any"
            },
            "gpuManufacturer": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "amd",
                    "apple",
                    "intel",
                    "nvidia",
                    "xilinx"
                  ]
                },
                {
                  "type": "null"
                }
              ],
              "description": "GPU vendor; null means any"
            },
            "gpuVramBytes": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Minimum VRAM per GPU, in bytes"
            },
            "ramBytes": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "RAM per instance, in bytes"
            },
            "storageBytes": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "Disk per instance, in bytes"
            },
            "shmemBytes": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Shared memory (/dev/shm) size, in bytes"
            }
          },
          "additionalProperties": false,
          "description": "Hardware requirements per instance"
        },
        "placement": {
          "type": "object",
          "properties": {
            "regions": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "us-west",
                  "us-east",
                  "us-central",
                  "ca-west",
                  "ca-east",
                  "ca-central",
                  "eu-west",
                  "eu-north",
                  "eu-east",
                  "eu-south",
                  "eu-central",
                  "eu-south-east",
                  "ap-east",
                  "ap-south-east",
                  "ap-north-east",
                  "ap-south"
                ]
              },
              "description": "Kinesis regions to run in; empty means any"
            },
            "providers": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "csp": {
                    "type": "string",
                    "enum": [
                      "aws",
                      "azure",
                      "gcp",
                      "kinesis"
                    ],
                    "description": "Cloud provider"
                  },
                  "regions": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "region": {
                          "type": "string",
                          "minLength": 1,
                          "description": "Provider region"
                        },
                        "zones": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Zones within the region; empty means any"
                        }
                      },
                      "required": [
                        "region",
                        "zones"
                      ],
                      "additionalProperties": false
                    },
                    "description": "Regions of the provider to allow"
                  }
                },
                "required": [
                  "csp",
                  "regions"
                ],
                "additionalProperties": false
              },
              "description": "Restrict placement to specific providers and regions"
            },
            "allowedIps": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Source IPs or CIDR ranges allowed to reach the app; empty means everyone"
            }
          },
          "additionalProperties": false,
          "description": "Where the app may run and who may reach it"
        },
        "grids": {
          "type": "object",
          "properties": {
            "ids": {
              "maxItems": 20,
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "description": "Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one"
            },
            "priorities": {
              "propertyNames": {
                "type": "string"
              },
              "description": "Priority per grid id, -100 to 100",
              "type": "object",
              "properties": {},
              "additionalProperties": {
                "type": "integer",
                "minimum": -100,
                "maximum": 100
              }
            }
          },
          "additionalProperties": false,
          "description": "Run on grids instead of serverless capacity (members only, on grids they may deploy to)"
        },
        "volumes": {
          "maxItems": 4,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$",
                "description": "Volume name, unique within the app"
              },
              "target": {
                "type": "string",
                "pattern": "^\\/.+",
                "description": "Absolute mount path inside the container"
              },
              "declaredSizeBytes": {
                "type": "integer",
                "minimum": 0,
                "maximum": 17592186044416,
                "description": "Expected size, used for placement; 0 or omitted means unspecified"
              }
            },
            "required": [
              "name",
              "target"
            ],
            "additionalProperties": false
          },
          "description": "Persistent volumes"
        }
      },
      "additionalProperties": false,
      "description": "Initial configuration; image is required unless source is set"
    },
    "source": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "github",
          "description": "Build the image from a GitHub repository"
        },
        "installationId": {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991,
          "description": "GitHub App installation id, from the connected accounts list"
        },
        "repo": {
          "type": "string",
          "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9._-]{1,100}$",
          "description": "Repository as owner/name; omit with clone"
        },
        "clone": {
          "type": "object",
          "properties": {
            "sourceRepo": {
              "type": "string",
              "minLength": 3,
              "maxLength": 300,
              "description": "Public GitHub repository to copy: a github.com URL (optionally .../tree/<branch>) or owner/name"
            },
            "sourceRef": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Branch, tag or commit to copy; default: the source's default branch"
            },
            "targetRepoName": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._-]{1,100}$",
              "description": "Name of the new repository under the connected account (installationId)"
            },
            "targetVisibility": {
              "type": "string",
              "enum": [
                "private",
                "public"
              ],
              "description": "Visibility of the new repository; private by default"
            }
          },
          "required": [
            "sourceRepo",
            "targetRepoName"
          ],
          "additionalProperties": false,
          "description": "Copy a public repository into the connected account and build from the copy, instead of repo"
        },
        "branch": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255,
          "description": "Branch to build and follow; default: the repository's default branch"
        },
        "contextDir": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Directory the image is built from, relative to the repository root (default /)"
        },
        "dockerfilePath": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Dockerfile path relative to contextDir (default Dockerfile)"
        },
        "autoDeploy": {
          "type": "boolean",
          "description": "Rebuild and redeploy on every push to the branch (default true)"
        },
        "dockerfileContent": {
          "type": "string",
          "maxLength": 65536,
          "description": "Inline Dockerfile built instead of dockerfilePath; how a repository without one is deployed"
        }
      },
      "required": [
        "kind",
        "installationId"
      ],
      "additionalProperties": false,
      "description": "Build the image from a GitHub repository the caller connected (members of the project's organization); omit to pull config.image"
    },
    "envFrom": {
      "type": "object",
      "properties": {
        "projectId": {
          "type": "string",
          "pattern": "^[0-9a-fA-F]{24}$",
          "description": "Project of the app to copy from"
        },
        "appId": {
          "type": "string",
          "pattern": "^[0-9a-fA-F]{24}$",
          "description": "App to copy the variables from"
        },
        "overrides": {
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
                "description": "Variable name"
              },
              "value": {
                "description": "Value; null keeps the copied value (a copied secret stays secret)",
                "type": [
                  "string",
                  "null"
                ]
              },
              "secret": {
                "type": "boolean",
                "description": "Store as a secret: never returned by reads"
              }
            },
            "required": [
              "key",
              "value"
            ],
            "additionalProperties": {}
          },
          "description": "Variables set on top of the copied ones, by key; a null value keeps the copied value"
        }
      },
      "required": [
        "projectId",
        "appId"
      ],
      "additionalProperties": false,
      "description": "Copy another app's environment variables, secret values included, on the server (developer access to that app); use overrides instead of config.env"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project to create the app in"
    },
    "idempotencyKey": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "pattern": "^\\S+$",
      "description": "Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice"
    }
  },
  "required": [
    "name",
    "config",
    "projectId"
  ],
  "additionalProperties": false
}
```

</details>

<details>

<summary>OpenAI profile input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "App name"
    },
    "config": {
      "type": "object",
      "properties": {
        "image": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Container image reference, e.g. nginx:1.27 or ghcr.io/org/app:tag"
        },
        "ports": {
          "maxItems": 32,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "port": {
                "type": "integer",
                "minimum": 1,
                "maximum": 65535,
                "description": "Container port"
              },
              "protocol": {
                "type": "string",
                "enum": [
                  "tcp",
                  "udp",
                  "tcp_load_balancer",
                  "tcps_load_balancer",
                  "http_load_balancer",
                  "https_load_balancer",
                  "http80_load_balancer",
                  "https443_load_balancer",
                  "grpcs_load_balancer"
                ],
                "description": "How the port is exposed"
              }
            },
            "required": [
              "port",
              "protocol"
            ],
            "additionalProperties": {}
          },
          "description": "Ports to expose"
        },
        "env": {
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
                "description": "Variable name"
              },
              "value": {
                "description": "Value; null keeps the stored value of an existing variable",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "key",
              "value"
            ],
            "additionalProperties": {}
          },
          "description": "Environment variables"
        },
        "command": {
          "maxItems": 256,
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Container command (CMD) override"
        },
        "entrypoint": {
          "maxItems": 256,
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Container entrypoint override"
        },
        "startupCommand": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "args": {
                  "minItems": 1,
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Command and arguments"
                },
                "user": {
                  "type": "string",
                  "description": "User to run as"
                },
                "timeoutSec": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Seconds before the command is considered failed"
                },
                "startPeriodSec": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Seconds to wait after the container starts"
                }
              },
              "required": [
                "args"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "Command run inside the container once it starts; null clears it"
        },
        "healthcheck": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "test": {
                  "minItems": 1,
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Command that must exit 0 for the app to count as healthy"
                },
                "intervalSec": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 9007199254740991,
                  "description": "Seconds between checks"
                }
              },
              "required": [
                "test",
                "intervalSec"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "Container health check; null clears it"
        },
        "restartPolicy": {
          "type": "object",
          "properties": {
            "mode": {
              "type": "string",
              "enum": [
                "disabled",
                "always",
                "on_failure",
                "task_completion"
              ],
              "description": "When to restart a container that exits"
            },
            "startTimeoutSec": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Seconds a start may take before it counts as failed"
            },
            "maxRetriesPerNode": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Restarts allowed on one server before moving on"
            },
            "maxErrorTolerance": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Failures tolerated before the app is stopped"
            },
            "numTasks": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "For task_completion: runs to complete before stopping"
            }
          },
          "required": [
            "mode"
          ],
          "additionalProperties": false,
          "description": "Restart behaviour"
        },
        "instances": {
          "type": "object",
          "properties": {
            "min": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Instances to keep running"
            },
            "max": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Upper bound for scaling"
            }
          },
          "required": [
            "min",
            "max"
          ],
          "additionalProperties": false,
          "description": "Instance count range"
        },
        "stateful": {
          "type": "boolean",
          "description": "Keep each instance pinned to its server and disk"
        },
        "hardware": {
          "type": "object",
          "properties": {
            "gpuCount": {
              "type": "integer",
              "minimum": 0,
              "maximum": 16,
              "description": "GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only"
            },
            "gpuModels": {
              "maxItems": 16,
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "description": "Acceptable GPU models, e.g. H100; empty means any"
            },
            "gpuManufacturer": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "amd",
                    "apple",
                    "intel",
                    "nvidia",
                    "xilinx"
                  ]
                },
                {
                  "type": "null"
                }
              ],
              "description": "GPU vendor; null means any"
            },
            "gpuVramBytes": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Minimum VRAM per GPU, in bytes"
            },
            "ramBytes": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "RAM per instance, in bytes"
            },
            "storageBytes": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "Disk per instance, in bytes"
            },
            "shmemBytes": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Shared memory (/dev/shm) size, in bytes"
            }
          },
          "additionalProperties": false,
          "description": "Hardware requirements per instance"
        },
        "placement": {
          "type": "object",
          "properties": {
            "regions": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "us-west",
                  "us-east",
                  "us-central",
                  "ca-west",
                  "ca-east",
                  "ca-central",
                  "eu-west",
                  "eu-north",
                  "eu-east",
                  "eu-south",
                  "eu-central",
                  "eu-south-east",
                  "ap-east",
                  "ap-south-east",
                  "ap-north-east",
                  "ap-south"
                ]
              },
              "description": "Kinesis regions to run in; empty means any"
            },
            "providers": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "csp": {
                    "type": "string",
                    "enum": [
                      "aws",
                      "azure",
                      "gcp",
                      "kinesis"
                    ],
                    "description": "Cloud provider"
                  },
                  "regions": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "region": {
                          "type": "string",
                          "minLength": 1,
                          "description": "Provider region"
                        },
                        "zones": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Zones within the region; empty means any"
                        }
                      },
                      "required": [
                        "region",
                        "zones"
                      ],
                      "additionalProperties": false
                    },
                    "description": "Regions of the provider to allow"
                  }
                },
                "required": [
                  "csp",
                  "regions"
                ],
                "additionalProperties": false
              },
              "description": "Restrict placement to specific providers and regions"
            },
            "allowedIps": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Source IPs or CIDR ranges allowed to reach the app; empty means everyone"
            }
          },
          "additionalProperties": false,
          "description": "Where the app may run and who may reach it"
        },
        "grids": {
          "type": "object",
          "properties": {
            "ids": {
              "maxItems": 20,
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "description": "Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one"
            },
            "priorities": {
              "propertyNames": {
                "type": "string"
              },
              "description": "Priority per grid id, -100 to 100",
              "type": "object",
              "properties": {},
              "additionalProperties": {
                "type": "integer",
                "minimum": -100,
                "maximum": 100
              }
            }
          },
          "additionalProperties": false,
          "description": "Run on grids instead of serverless capacity (members only, on grids they may deploy to)"
        },
        "volumes": {
          "maxItems": 4,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$",
                "description": "Volume name, unique within the app"
              },
              "target": {
                "type": "string",
                "pattern": "^\\/.+",
                "description": "Absolute mount path inside the container"
              },
              "declaredSizeBytes": {
                "type": "integer",
                "minimum": 0,
                "maximum": 17592186044416,
                "description": "Expected size, used for placement; 0 or omitted means unspecified"
              }
            },
            "required": [
              "name",
              "target"
            ],
            "additionalProperties": false
          },
          "description": "Persistent volumes"
        }
      },
      "additionalProperties": false,
      "description": "Initial configuration; image is required unless source is set"
    },
    "source": {
      "type": "object",
      "properties": {
        "kind": {
          "type": "string",
          "const": "github",
          "description": "Build the image from a GitHub repository"
        },
        "installationId": {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991,
          "description": "GitHub App installation id, from the connected accounts list"
        },
        "repo": {
          "type": "string",
          "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9._-]{1,100}$",
          "description": "Repository as owner/name; omit with clone"
        },
        "clone": {
          "type": "object",
          "properties": {
            "sourceRepo": {
              "type": "string",
              "minLength": 3,
              "maxLength": 300,
              "description": "Public GitHub repository to copy: a github.com URL (optionally .../tree/<branch>) or owner/name"
            },
            "sourceRef": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Branch, tag or commit to copy; default: the source's default branch"
            },
            "targetRepoName": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._-]{1,100}$",
              "description": "Name of the new repository under the connected account (installationId)"
            },
            "targetVisibility": {
              "type": "string",
              "enum": [
                "private",
                "public"
              ],
              "description": "Visibility of the new repository; private by default"
            }
          },
          "required": [
            "sourceRepo",
            "targetRepoName"
          ],
          "additionalProperties": false,
          "description": "Copy a public repository into the connected account and build from the copy, instead of repo"
        },
        "branch": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255,
          "description": "Branch to build and follow; default: the repository's default branch"
        },
        "contextDir": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Directory the image is built from, relative to the repository root (default /)"
        },
        "dockerfilePath": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Dockerfile path relative to contextDir (default Dockerfile)"
        },
        "autoDeploy": {
          "type": "boolean",
          "description": "Rebuild and redeploy on every push to the branch (default true)"
        },
        "dockerfileContent": {
          "type": "string",
          "maxLength": 65536,
          "description": "Inline Dockerfile built instead of dockerfilePath; how a repository without one is deployed"
        }
      },
      "required": [
        "kind",
        "installationId"
      ],
      "additionalProperties": false,
      "description": "Build the image from a GitHub repository the caller connected (members of the project's organization); omit to pull config.image"
    },
    "envFrom": {
      "type": "object",
      "properties": {
        "projectId": {
          "type": "string",
          "pattern": "^[0-9a-fA-F]{24}$",
          "description": "Project of the app to copy from"
        },
        "appId": {
          "type": "string",
          "pattern": "^[0-9a-fA-F]{24}$",
          "description": "App to copy the variables from"
        },
        "overrides": {
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
                "description": "Variable name"
              },
              "value": {
                "description": "Value; null keeps the copied value (a copied secret stays secret)",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "key",
              "value"
            ],
            "additionalProperties": {}
          },
          "description": "Variables set on top of the copied ones, by key; a null value keeps the copied value"
        }
      },
      "required": [
        "projectId",
        "appId"
      ],
      "additionalProperties": false,
      "description": "Copy another app's environment variables, secret values included, on the server (developer access to that app); use overrides instead of config.env"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project to create the app in"
    },
    "idempotencyKey": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "pattern": "^\\S+$",
      "description": "Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice"
    }
  },
  "required": [
    "name",
    "config",
    "projectId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): App id
* `projectId` (`string`): See the complete response schema below.
* `name` (`string`): See the complete response schema below.
* `guid` (`string`): Runtime identifier of the app
* `source` (`string or null`): Where the image comes from: Docker Hub, a registry link, GitHub or an upload
* `image` (`string or null`): See the complete response schema below.
* `domain` (`string or null`): Hostname the app answers on: the default custom domain, else the Kinesis-issued one
* `buildState` (`string or null`): Build pipeline state for GitHub-sourced apps
* `access` (`object`): See the complete response schema below.
* `createdBy` (`object or null`): Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read
* `createdAt` (`string`): ISO 8601 timestamp (UTC)
* `status` (`object`): Status as last cached; use the status endpoint for a live read
* `config` (`object or null`): See the complete response schema below.
* `github` (`object or null`): Repository settings of a GitHub-sourced app
* `defaultDomain` (`string or null`): Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one
* `start` (`object`): How the automatic start after creation went
* `buildId` (`string or null`): First build of a GitHub-sourced app; null for image apps

<details>

<summary>Response schema: apps.create</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "App id"
    },
    "projectId": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "guid": {
      "type": "string",
      "description": "Runtime identifier of the app"
    },
    "source": {
      "description": "Where the image comes from: Docker Hub, a registry link, GitHub or an upload",
      "type": [
        "string",
        "null"
      ]
    },
    "image": {
      "type": [
        "string",
        "null"
      ]
    },
    "domain": {
      "description": "Hostname the app answers on: the default custom domain, else the Kinesis-issued one",
      "type": [
        "string",
        "null"
      ]
    },
    "buildState": {
      "description": "Build pipeline state for GitHub-sourced apps",
      "type": [
        "string",
        "null"
      ]
    },
    "access": {
      "type": "object",
      "properties": {
        "role": {
          "type": "string",
          "enum": [
            "admin",
            "developer",
            "viewer"
          ],
          "description": "The caller's role on it"
        },
        "outside": {
          "type": "boolean",
          "description": "Shared with the caller from another organization"
        },
        "isCreator": {
          "type": "boolean",
          "description": "The caller created it"
        }
      },
      "required": [
        "role",
        "outside",
        "isCreator"
      ],
      "additionalProperties": false
    },
    "createdBy": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "userId": {
              "type": "string",
              "description": "User id, as members.list and the sharing calls use it"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "email": {
              "type": "string"
            }
          },
          "required": [
            "userId",
            "name",
            "email"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    },
    "status": {
      "type": "object",
      "properties": {
        "state": {
          "type": "string",
          "enum": [
            "unknown",
            "running",
            "stopped",
            "starting",
            "stopping",
            "scaling",
            "updating",
            "downloading",
            "provisioning",
            "start_blocked",
            "error",
            "building",
            "build_failed"
          ],
          "description": "Aggregate state; build states win while there is no runnable image"
        },
        "targetState": {
          "type": "string",
          "enum": [
            "running",
            "stopped",
            "unknown"
          ],
          "description": "What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"
        },
        "nodes": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "address": {
                "type": "string"
              },
              "state": {
                "type": "string"
              },
              "reportedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "When the server last reported this state; not a start time"
              }
            },
            "required": [
              "address",
              "state",
              "reportedAt"
            ],
            "additionalProperties": false
          },
          "description": "Servers the app is placed on and the container state on each"
        },
        "endpoints": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "port": {
                "type": "integer",
                "description": "Container port, as in config.ports"
              },
              "protocol": {
                "type": "string",
                "description": "Protocol of that port, as in config.ports"
              },
              "address": {
                "description": "An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired",
                "type": [
                  "string",
                  "null"
                ]
              },
              "server": {
                "description": "Server address of a direct tcp or udp endpoint; null when load-balanced",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "port",
              "protocol",
              "address",
              "server"
            ],
            "additionalProperties": false
          },
          "description": "Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"
        },
        "errors": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "at": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "details": {
                "anyOf": [
                  {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {}
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Extra data the runtime attached, such as the grid or server involved"
              }
            },
            "required": [
              "type",
              "message",
              "at",
              "details"
            ],
            "additionalProperties": false
          },
          "description": "Recent runtime errors, newest first"
        },
        "workOrders": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "description": "What the runtime asked the server to do"
              },
              "server": {
                "type": "string",
                "description": "Server address"
              },
              "status": {
                "type": "string"
              },
              "createdAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "startedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "completedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "type",
              "server",
              "status",
              "createdAt",
              "startedAt",
              "completedAt"
            ],
            "additionalProperties": false
          },
          "description": "The runtime's latest instructions to servers, newest first; an order that never completes is the stall"
        },
        "grid": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "fill": {
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "target": {
                          "type": "integer",
                          "description": "Members the grid is composed to have"
                        },
                        "online": {
                          "type": "integer",
                          "description": "Members the app runs on"
                        },
                        "pending": {
                          "type": "integer"
                        },
                        "converging": {
                          "type": "boolean",
                          "description": "The grid is still acquiring members or placing the app"
                        }
                      },
                      "required": [
                        "target",
                        "online",
                        "pending",
                        "converging"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "How far the grid has filled for this app"
                },
                "idleReclaim": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "gridId": {
                        "type": "string"
                      },
                      "warnedAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "evictAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "When the app becomes evictable; null when the grid only warns"
                      },
                      "evictedAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "cooldownEndsAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "When the grid takes the app again after an eviction"
                      },
                      "evidence": {
                        "type": "string",
                        "description": "Why the grid considers the app idle"
                      }
                    },
                    "required": [
                      "gridId",
                      "warnedAt",
                      "evictAt",
                      "evictedAt",
                      "cooldownEndsAt",
                      "evidence"
                    ],
                    "additionalProperties": false
                  },
                  "description": "Grids whose idle policy has warned or evicted the app"
                },
                "queue": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "gridId": {
                        "type": "string"
                      },
                      "position": {
                        "type": "integer",
                        "description": "1-based place in the grid's fair-share queue"
                      },
                      "queueLength": {
                        "type": "integer"
                      },
                      "enqueuedAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "held": {
                        "type": "boolean",
                        "description": "The scheduler keeps the app off the grid until its turn comes"
                      }
                    },
                    "required": [
                      "gridId",
                      "position",
                      "queueLength",
                      "enqueuedAt",
                      "held"
                    ],
                    "additionalProperties": false
                  },
                  "description": "Full grids the app is queued on"
                }
              },
              "required": [
                "fill",
                "idleReclaim",
                "queue"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "Grid placement details; null for a serverless app"
        },
        "pollAfterSec": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "description": "Seconds to wait before reading the status again while it is changing; null in a settled state"
        }
      },
      "required": [
        "state",
        "targetState",
        "nodes",
        "endpoints",
        "errors",
        "workOrders",
        "grid",
        "pollAfterSec"
      ],
      "additionalProperties": false,
      "description": "Status as last cached; use the status endpoint for a live read"
    },
    "config": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "image": {
              "type": "string",
              "description": "Container image reference"
            },
            "imageTag": {
              "description": "Tag, kept separately for Docker Hub images",
              "type": [
                "string",
                "null"
              ]
            },
            "registryUsername": {
              "description": "Registry login; the password is write-only",
              "type": [
                "string",
                "null"
              ]
            },
            "ports": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "port": {
                    "type": "integer"
                  },
                  "protocol": {
                    "type": "string"
                  }
                },
                "required": [
                  "port",
                  "protocol"
                ],
                "additionalProperties": false
              }
            },
            "env": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string"
                  },
                  "value": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "secret": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "key",
                  "value",
                  "secret"
                ],
                "additionalProperties": false
              },
              "description": "Environment variables; value is null for secrets, and on a connection that takes no credentials for values that look like one (null keeps a value on update)"
            },
            "command": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "entrypoint": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "startupCommand": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "args": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "user": {
                      "type": "string"
                    },
                    "timeoutSec": {
                      "type": "number"
                    },
                    "startPeriodSec": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "args",
                    "user",
                    "timeoutSec",
                    "startPeriodSec"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "healthcheck": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "test": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "intervalSec": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "test",
                    "intervalSec"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "restartPolicy": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "mode": {
                      "type": "string"
                    },
                    "startTimeoutSec": {
                      "type": "number"
                    },
                    "maxRetriesPerNode": {
                      "type": "number"
                    },
                    "maxErrorTolerance": {
                      "type": "number"
                    },
                    "numTasks": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "mode",
                    "startTimeoutSec",
                    "maxRetriesPerNode",
                    "maxErrorTolerance",
                    "numTasks"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "instances": {
              "type": "object",
              "properties": {
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                }
              },
              "required": [
                "min",
                "max"
              ],
              "additionalProperties": false
            },
            "stateful": {
              "type": "boolean"
            },
            "hardware": {
              "type": "object",
              "properties": {
                "gpuCount": {
                  "type": "integer"
                },
                "gpuModels": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "gpuManufacturer": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "gpuVramBytes": {
                  "type": "number"
                },
                "ramBytes": {
                  "type": "number"
                },
                "storageBytes": {
                  "type": "number"
                },
                "shmemBytes": {
                  "type": "number"
                }
              },
              "required": [
                "gpuCount",
                "gpuModels",
                "gpuManufacturer",
                "gpuVramBytes",
                "ramBytes",
                "storageBytes",
                "shmemBytes"
              ],
              "additionalProperties": false
            },
            "placement": {
              "type": "object",
              "properties": {
                "regions": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Kinesis regions, e.g. us-west"
                },
                "providers": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "csp": {
                        "type": "string"
                      },
                      "regions": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "region": {
                              "type": "string"
                            },
                            "zones": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          },
                          "required": [
                            "region",
                            "zones"
                          ],
                          "additionalProperties": false
                        }
                      }
                    },
                    "required": [
                      "csp",
                      "regions"
                    ],
                    "additionalProperties": false
                  }
                },
                "allowedIps": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Source IPs or CIDR ranges allowed to reach the app"
                }
              },
              "required": [
                "regions",
                "providers",
                "allowedIps"
              ],
              "additionalProperties": false
            },
            "grids": {
              "type": "object",
              "properties": {
                "ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "priorities": {
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {
                    "type": "number"
                  },
                  "description": "Priority per grid id"
                }
              },
              "required": [
                "ids",
                "priorities"
              ],
              "additionalProperties": false
            },
            "volumes": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "target": {
                    "type": "string"
                  },
                  "declaredSizeBytes": {
                    "type": "number"
                  }
                },
                "required": [
                  "name",
                  "target",
                  "declaredSizeBytes"
                ],
                "additionalProperties": false
              }
            },
            "domains": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Custom domains attached to the app (see the domain operations)"
            }
          },
          "required": [
            "image",
            "imageTag",
            "registryUsername",
            "ports",
            "env",
            "command",
            "entrypoint",
            "startupCommand",
            "healthcheck",
            "restartPolicy",
            "instances",
            "stateful",
            "hardware",
            "placement",
            "grids",
            "volumes",
            "domains"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ]
    },
    "github": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "installationId": {
              "type": "integer",
              "description": "GitHub App installation the repository is read through"
            },
            "repo": {
              "type": "string",
              "description": "owner/name"
            },
            "url": {
              "type": "string"
            },
            "branch": {
              "type": "string",
              "description": "Branch that is built and, with autoDeploy, followed"
            },
            "contextDir": {
              "type": "string"
            },
            "dockerfilePath": {
              "description": "Dockerfile path relative to contextDir; not used while dockerfileContent is set",
              "type": [
                "string",
                "null"
              ]
            },
            "dockerfileOverride": {
              "type": "boolean",
              "description": "An inline Dockerfile (dockerfileContent) is built instead of dockerfilePath"
            },
            "dockerfileContent": {
              "description": "The inline Dockerfile that is built; null when the repository's own file is used",
              "type": [
                "string",
                "null"
              ]
            },
            "autoDeploy": {
              "type": "boolean",
              "description": "Every push to the branch rebuilds and redeploys"
            },
            "clonedFrom": {
              "description": "Public repository (owner/name) the app's repository was copied from at creation; null otherwise",
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "installationId",
            "repo",
            "url",
            "branch",
            "contextDir",
            "dockerfilePath",
            "dockerfileOverride",
            "dockerfileContent",
            "autoDeploy",
            "clonedFrom"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Repository settings of a GitHub-sourced app"
    },
    "defaultDomain": {
      "description": "Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one",
      "type": [
        "string",
        "null"
      ]
    },
    "start": {
      "type": "object",
      "properties": {
        "state": {
          "type": "string",
          "enum": [
            "requested",
            "deferred",
            "building",
            "failed"
          ],
          "description": "requested: the start was accepted, poll the status to follow it; deferred: machines are being provisioned for the app first, poll the status; building: the app starts once its first build succeeds, poll the build; failed: the app exists but is stopped, see error"
        },
        "error": {
          "description": "Why the automatic start failed",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "state",
        "error"
      ],
      "additionalProperties": false,
      "description": "How the automatic start after creation went"
    },
    "buildId": {
      "description": "First build of a GitHub-sourced app; null for image apps",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "id",
    "projectId",
    "name",
    "guid",
    "source",
    "image",
    "domain",
    "buildState",
    "access",
    "createdBy",
    "createdAt",
    "status",
    "config",
    "github",
    "defaultDomain",
    "start",
    "buildId"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `402 balance_over_limit`, `429 app_creation_quota_exceeded`, `400 no_suitable_machines`, `400 serverless_gpu_unavailable`, `429 grid_quota_exceeded`, `403 serverless_disabled`, `503 github_not_configured`, `404 github_installation_not_found`, `404 github_repo_not_found`, `502 upstream_github`, `404 github_branch_not_found`, `409 app_config_unavailable`.

## `update_app`

Update the name, alerts, configuration or repository settings of an app. A running app is redeployed with the new configuration; while a GitHub-sourced app is still building, the change is applied by that build. Changing instances, hardware or volumes also needs compute:spend.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `update_app`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: true`

### Permissions and REST equivalents

| Operation     | REST                                                                                 | Required scopes |
| ------------- | ------------------------------------------------------------------------------------ | --------------- |
| `apps.update` | `PATCH /projects/{projectId}/apps/{appId}`                                           | `apps:write`    |
| Conditional   | instances, hardware or volumes change                                                | `compute:spend` |
| Conditional   | source changes what is built (branch, contextDir, dockerfilePath, dockerfileContent) | `compute:spend` |

### Arguments

| Argument    | Type     | Required | Description                                                                                                                                             |
| ----------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`      | `string` | No       | New app name                                                                                                                                            |
| `alerts`    | `object` | No       | Health-check email alerts                                                                                                                               |
| `config`    | `object` | No       | Configuration changes; list fields replace the stored list, object fields merge per key                                                                 |
| `source`    | `object` | No       | Repository settings of a GitHub-sourced app (members of the project's organization); a change to the branch, directories or Dockerfile starts a rebuild |
| `projectId` | `string` | Yes      | Project id                                                                                                                                              |
| `appId`     | `string` | Yes      | App id                                                                                                                                                  |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "New app name"
    },
    "alerts": {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "Send an email when the app is unhealthy"
        },
        "emails": {
          "maxItems": 10,
          "type": "array",
          "items": {
            "type": "string",
            "format": "email",
            "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
          },
          "description": "Recipients"
        },
        "delayMinutes": {
          "type": "integer",
          "minimum": 0,
          "maximum": 30,
          "description": "Minutes the app must stay unhealthy before an email"
        }
      },
      "required": [
        "enabled",
        "emails",
        "delayMinutes"
      ],
      "additionalProperties": false,
      "description": "Health-check email alerts"
    },
    "config": {
      "type": "object",
      "properties": {
        "image": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Container image reference, e.g. nginx:1.27 or ghcr.io/org/app:tag"
        },
        "registryAuth": {
          "type": "object",
          "properties": {
            "username": {
              "type": "string",
              "minLength": 1,
              "description": "Registry login"
            },
            "password": {
              "type": "string",
              "minLength": 1,
              "description": "Registry password or token (write-only)"
            }
          },
          "required": [
            "username",
            "password"
          ],
          "additionalProperties": false,
          "description": "Credentials for a private image"
        },
        "ports": {
          "maxItems": 32,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "port": {
                "type": "integer",
                "minimum": 1,
                "maximum": 65535,
                "description": "Container port"
              },
              "protocol": {
                "type": "string",
                "enum": [
                  "tcp",
                  "udp",
                  "tcp_load_balancer",
                  "tcps_load_balancer",
                  "http_load_balancer",
                  "https_load_balancer",
                  "http80_load_balancer",
                  "https443_load_balancer",
                  "grpcs_load_balancer"
                ],
                "description": "How the port is exposed"
              }
            },
            "required": [
              "port",
              "protocol"
            ],
            "additionalProperties": {}
          },
          "description": "Ports to expose"
        },
        "env": {
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
                "description": "Variable name"
              },
              "value": {
                "description": "Value; null keeps the stored value of an existing variable",
                "type": [
                  "string",
                  "null"
                ]
              },
              "secret": {
                "type": "boolean",
                "description": "Store as a secret: never returned by reads"
              }
            },
            "required": [
              "key",
              "value"
            ],
            "additionalProperties": {}
          },
          "description": "Environment variables"
        },
        "command": {
          "maxItems": 256,
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Container command (CMD) override"
        },
        "entrypoint": {
          "maxItems": 256,
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Container entrypoint override"
        },
        "startupCommand": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "args": {
                  "minItems": 1,
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Command and arguments"
                },
                "user": {
                  "type": "string",
                  "description": "User to run as"
                },
                "timeoutSec": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Seconds before the command is considered failed"
                },
                "startPeriodSec": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 9007199254740991,
                  "description": "Seconds to wait after the container starts"
                }
              },
              "required": [
                "args"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "Command run inside the container once it starts; null clears it"
        },
        "healthcheck": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "test": {
                  "minItems": 1,
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Command that must exit 0 for the app to count as healthy"
                },
                "intervalSec": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 9007199254740991,
                  "description": "Seconds between checks"
                }
              },
              "required": [
                "test",
                "intervalSec"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "Container health check; null clears it"
        },
        "restartPolicy": {
          "type": "object",
          "properties": {
            "mode": {
              "type": "string",
              "enum": [
                "disabled",
                "always",
                "on_failure",
                "task_completion"
              ],
              "description": "When to restart a container that exits"
            },
            "startTimeoutSec": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Seconds a start may take before it counts as failed"
            },
            "maxRetriesPerNode": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Restarts allowed on one server before moving on"
            },
            "maxErrorTolerance": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Failures tolerated before the app is stopped"
            },
            "numTasks": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "For task_completion: runs to complete before stopping"
            }
          },
          "required": [
            "mode"
          ],
          "additionalProperties": false,
          "description": "Restart behaviour"
        },
        "instances": {
          "type": "object",
          "properties": {
            "min": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Instances to keep running"
            },
            "max": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Upper bound for scaling"
            }
          },
          "required": [
            "min",
            "max"
          ],
          "additionalProperties": false,
          "description": "Instance count range"
        },
        "stateful": {
          "type": "boolean",
          "description": "Keep each instance pinned to its server and disk"
        },
        "hardware": {
          "type": "object",
          "properties": {
            "gpuCount": {
              "type": "integer",
              "minimum": 0,
              "maximum": 16,
              "description": "GPUs per instance; 0 for CPU-only. Above 0 needs grids.ids: serverless placement is CPU-only"
            },
            "gpuModels": {
              "maxItems": 16,
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "description": "Acceptable GPU models, e.g. H100; empty means any"
            },
            "gpuManufacturer": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "amd",
                    "apple",
                    "intel",
                    "nvidia",
                    "xilinx"
                  ]
                },
                {
                  "type": "null"
                }
              ],
              "description": "GPU vendor; null means any"
            },
            "gpuVramBytes": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Minimum VRAM per GPU, in bytes"
            },
            "ramBytes": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "RAM per instance, in bytes"
            },
            "storageBytes": {
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991,
              "description": "Disk per instance, in bytes"
            },
            "shmemBytes": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991,
              "description": "Shared memory (/dev/shm) size, in bytes"
            }
          },
          "additionalProperties": false,
          "description": "Hardware requirements per instance"
        },
        "placement": {
          "type": "object",
          "properties": {
            "regions": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "us-west",
                  "us-east",
                  "us-central",
                  "ca-west",
                  "ca-east",
                  "ca-central",
                  "eu-west",
                  "eu-north",
                  "eu-east",
                  "eu-south",
                  "eu-central",
                  "eu-south-east",
                  "ap-east",
                  "ap-south-east",
                  "ap-north-east",
                  "ap-south"
                ]
              },
              "description": "Kinesis regions to run in; empty means any"
            },
            "providers": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "csp": {
                    "type": "string",
                    "enum": [
                      "aws",
                      "azure",
                      "gcp",
                      "kinesis"
                    ],
                    "description": "Cloud provider"
                  },
                  "regions": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "region": {
                          "type": "string",
                          "minLength": 1,
                          "description": "Provider region"
                        },
                        "zones": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Zones within the region; empty means any"
                        }
                      },
                      "required": [
                        "region",
                        "zones"
                      ],
                      "additionalProperties": false
                    },
                    "description": "Regions of the provider to allow"
                  }
                },
                "required": [
                  "csp",
                  "regions"
                ],
                "additionalProperties": false
              },
              "description": "Restrict placement to specific providers and regions"
            },
            "allowedIps": {
              "maxItems": 100,
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Source IPs or CIDR ranges allowed to reach the app; empty means everyone"
            }
          },
          "additionalProperties": false,
          "description": "Where the app may run and who may reach it"
        },
        "grids": {
          "type": "object",
          "properties": {
            "ids": {
              "maxItems": 20,
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "description": "Grids to run on; empty means serverless (CPU-only), so a GPU app names at least one"
            },
            "priorities": {
              "propertyNames": {
                "type": "string"
              },
              "description": "Priority per grid id, -100 to 100",
              "type": "object",
              "properties": {},
              "additionalProperties": {
                "type": "integer",
                "minimum": -100,
                "maximum": 100
              }
            }
          },
          "additionalProperties": false,
          "description": "Run on grids instead of serverless capacity (members only, on grids they may deploy to)"
        },
        "volumes": {
          "maxItems": 4,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$",
                "description": "Volume name, unique within the app"
              },
              "target": {
                "type": "string",
                "pattern": "^\\/.+",
                "description": "Absolute mount path inside the container"
              },
              "declaredSizeBytes": {
                "type": "integer",
                "minimum": 0,
                "maximum": 17592186044416,
                "description": "Expected size, used for placement; 0 or omitted means unspecified"
              }
            },
            "required": [
              "name",
              "target"
            ],
            "additionalProperties": false
          },
          "description": "Persistent volumes"
        }
      },
      "additionalProperties": false,
      "description": "Configuration changes; list fields replace the stored list, object fields merge per key"
    },
    "source": {
      "type": "object",
      "properties": {
        "branch": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255,
          "description": "Branch to build and follow; default: the repository's default branch"
        },
        "contextDir": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Directory the image is built from, relative to the repository root (default /)"
        },
        "dockerfilePath": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Dockerfile path relative to contextDir (default Dockerfile)"
        },
        "autoDeploy": {
          "type": "boolean",
          "description": "Rebuild and redeploy on every push to the branch (default true)"
        },
        "dockerfileContent": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 65536
            },
            {
              "type": "null"
            }
          ],
          "description": "Inline Dockerfile used instead of dockerfilePath; null goes back to the file in the repository"
        }
      },
      "additionalProperties": false,
      "description": "Repository settings of a GitHub-sourced app (members of the project's organization); a change to the branch, directories or Dockerfile starts a rebuild"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

<details>

<summary>OpenAI profile input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "New app name"
    },
    "alerts": {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "Send an email when the app is unhealthy"
        },
        "emails": {
          "maxItems": 10,
          "type": "array",
          "items": {
            "type": "string",
            "format": "email",
            "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
          },
          "description": "Recipients"
        },
        "delayMinutes": {
          "type": "integer",
          "minimum": 0,
          "maximum": 30,
          "description": "Minutes the app must stay unhealthy before an email"
        }
      },
      "required": [
        "enabled",
        "emails",
        "delayMinutes"
      ],
      "additionalProperties": false,
      "description": "Health-check email alerts"
    },
    "config": {
      "type": "object",
      "description": "The same fields as create_app's config; list fields replace the stored list, object fields merge per key"
    },
    "source": {
      "type": "object",
      "properties": {
        "branch": {
          "type": "string",
          "minLength": 1,
          "maxLength": 255,
          "description": "Branch to build and follow; default: the repository's default branch"
        },
        "contextDir": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Directory the image is built from, relative to the repository root (default /)"
        },
        "dockerfilePath": {
          "type": "string",
          "minLength": 1,
          "maxLength": 512,
          "description": "Dockerfile path relative to contextDir (default Dockerfile)"
        },
        "autoDeploy": {
          "type": "boolean",
          "description": "Rebuild and redeploy on every push to the branch (default true)"
        },
        "dockerfileContent": {
          "anyOf": [
            {
              "type": "string",
              "maxLength": 65536
            },
            {
              "type": "null"
            }
          ],
          "description": "Inline Dockerfile used instead of dockerfilePath; null goes back to the file in the repository"
        }
      },
      "additionalProperties": false,
      "description": "Repository settings of a GitHub-sourced app (members of the project's organization); a change to the branch, directories or Dockerfile starts a rebuild"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): App id
* `projectId` (`string`): See the complete response schema below.
* `name` (`string`): See the complete response schema below.
* `guid` (`string`): Runtime identifier of the app
* `source` (`string or null`): Where the image comes from: Docker Hub, a registry link, GitHub or an upload
* `image` (`string or null`): See the complete response schema below.
* `domain` (`string or null`): Hostname the app answers on: the default custom domain, else the Kinesis-issued one
* `buildState` (`string or null`): Build pipeline state for GitHub-sourced apps
* `access` (`object`): See the complete response schema below.
* `createdBy` (`object or null`): Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read
* `createdAt` (`string`): ISO 8601 timestamp (UTC)
* `status` (`object`): Status as last cached; use the status endpoint for a live read
* `config` (`object or null`): See the complete response schema below.
* `github` (`object or null`): Repository settings of a GitHub-sourced app
* `defaultDomain` (`string or null`): Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one
* `buildId` (`string or null`): Build started by a source change; null when nothing was rebuilt

<details>

<summary>Response schema: apps.update</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "App id"
    },
    "projectId": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "guid": {
      "type": "string",
      "description": "Runtime identifier of the app"
    },
    "source": {
      "description": "Where the image comes from: Docker Hub, a registry link, GitHub or an upload",
      "type": [
        "string",
        "null"
      ]
    },
    "image": {
      "type": [
        "string",
        "null"
      ]
    },
    "domain": {
      "description": "Hostname the app answers on: the default custom domain, else the Kinesis-issued one",
      "type": [
        "string",
        "null"
      ]
    },
    "buildState": {
      "description": "Build pipeline state for GitHub-sourced apps",
      "type": [
        "string",
        "null"
      ]
    },
    "access": {
      "type": "object",
      "properties": {
        "role": {
          "type": "string",
          "enum": [
            "admin",
            "developer",
            "viewer"
          ],
          "description": "The caller's role on it"
        },
        "outside": {
          "type": "boolean",
          "description": "Shared with the caller from another organization"
        },
        "isCreator": {
          "type": "boolean",
          "description": "The caller created it"
        }
      },
      "required": [
        "role",
        "outside",
        "isCreator"
      ],
      "additionalProperties": false
    },
    "createdBy": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "userId": {
              "type": "string",
              "description": "User id, as members.list and the sharing calls use it"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "email": {
              "type": "string"
            }
          },
          "required": [
            "userId",
            "name",
            "email"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    },
    "status": {
      "type": "object",
      "properties": {
        "state": {
          "type": "string",
          "enum": [
            "unknown",
            "running",
            "stopped",
            "starting",
            "stopping",
            "scaling",
            "updating",
            "downloading",
            "provisioning",
            "start_blocked",
            "error",
            "building",
            "build_failed"
          ],
          "description": "Aggregate state; build states win while there is no runnable image"
        },
        "targetState": {
          "type": "string",
          "enum": [
            "running",
            "stopped",
            "unknown"
          ],
          "description": "What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"
        },
        "nodes": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "address": {
                "type": "string"
              },
              "state": {
                "type": "string"
              },
              "reportedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "When the server last reported this state; not a start time"
              }
            },
            "required": [
              "address",
              "state",
              "reportedAt"
            ],
            "additionalProperties": false
          },
          "description": "Servers the app is placed on and the container state on each"
        },
        "endpoints": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "port": {
                "type": "integer",
                "description": "Container port, as in config.ports"
              },
              "protocol": {
                "type": "string",
                "description": "Protocol of that port, as in config.ports"
              },
              "address": {
                "description": "An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired",
                "type": [
                  "string",
                  "null"
                ]
              },
              "server": {
                "description": "Server address of a direct tcp or udp endpoint; null when load-balanced",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "port",
              "protocol",
              "address",
              "server"
            ],
            "additionalProperties": false
          },
          "description": "Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"
        },
        "errors": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "at": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "details": {
                "anyOf": [
                  {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {}
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "Extra data the runtime attached, such as the grid or server involved"
              }
            },
            "required": [
              "type",
              "message",
              "at",
              "details"
            ],
            "additionalProperties": false
          },
          "description": "Recent runtime errors, newest first"
        },
        "workOrders": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "description": "What the runtime asked the server to do"
              },
              "server": {
                "type": "string",
                "description": "Server address"
              },
              "status": {
                "type": "string"
              },
              "createdAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "startedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "completedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 timestamp (UTC)"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "type",
              "server",
              "status",
              "createdAt",
              "startedAt",
              "completedAt"
            ],
            "additionalProperties": false
          },
          "description": "The runtime's latest instructions to servers, newest first; an order that never completes is the stall"
        },
        "grid": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "fill": {
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "target": {
                          "type": "integer",
                          "description": "Members the grid is composed to have"
                        },
                        "online": {
                          "type": "integer",
                          "description": "Members the app runs on"
                        },
                        "pending": {
                          "type": "integer"
                        },
                        "converging": {
                          "type": "boolean",
                          "description": "The grid is still acquiring members or placing the app"
                        }
                      },
                      "required": [
                        "target",
                        "online",
                        "pending",
                        "converging"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "null"
                    }
                  ],
                  "description": "How far the grid has filled for this app"
                },
                "idleReclaim": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "gridId": {
                        "type": "string"
                      },
                      "warnedAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "evictAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "When the app becomes evictable; null when the grid only warns"
                      },
                      "evictedAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "cooldownEndsAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "When the grid takes the app again after an eviction"
                      },
                      "evidence": {
                        "type": "string",
                        "description": "Why the grid considers the app idle"
                      }
                    },
                    "required": [
                      "gridId",
                      "warnedAt",
                      "evictAt",
                      "evictedAt",
                      "cooldownEndsAt",
                      "evidence"
                    ],
                    "additionalProperties": false
                  },
                  "description": "Grids whose idle policy has warned or evicted the app"
                },
                "queue": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "gridId": {
                        "type": "string"
                      },
                      "position": {
                        "type": "integer",
                        "description": "1-based place in the grid's fair-share queue"
                      },
                      "queueLength": {
                        "type": "integer"
                      },
                      "enqueuedAt": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "description": "ISO 8601 timestamp (UTC)"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "held": {
                        "type": "boolean",
                        "description": "The scheduler keeps the app off the grid until its turn comes"
                      }
                    },
                    "required": [
                      "gridId",
                      "position",
                      "queueLength",
                      "enqueuedAt",
                      "held"
                    ],
                    "additionalProperties": false
                  },
                  "description": "Full grids the app is queued on"
                }
              },
              "required": [
                "fill",
                "idleReclaim",
                "queue"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "Grid placement details; null for a serverless app"
        },
        "pollAfterSec": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "description": "Seconds to wait before reading the status again while it is changing; null in a settled state"
        }
      },
      "required": [
        "state",
        "targetState",
        "nodes",
        "endpoints",
        "errors",
        "workOrders",
        "grid",
        "pollAfterSec"
      ],
      "additionalProperties": false,
      "description": "Status as last cached; use the status endpoint for a live read"
    },
    "config": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "image": {
              "type": "string",
              "description": "Container image reference"
            },
            "imageTag": {
              "description": "Tag, kept separately for Docker Hub images",
              "type": [
                "string",
                "null"
              ]
            },
            "registryUsername": {
              "description": "Registry login; the password is write-only",
              "type": [
                "string",
                "null"
              ]
            },
            "ports": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "port": {
                    "type": "integer"
                  },
                  "protocol": {
                    "type": "string"
                  }
                },
                "required": [
                  "port",
                  "protocol"
                ],
                "additionalProperties": false
              }
            },
            "env": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string"
                  },
                  "value": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "secret": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "key",
                  "value",
                  "secret"
                ],
                "additionalProperties": false
              },
              "description": "Environment variables; value is null for secrets, and on a connection that takes no credentials for values that look like one (null keeps a value on update)"
            },
            "command": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "entrypoint": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "startupCommand": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "args": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "user": {
                      "type": "string"
                    },
                    "timeoutSec": {
                      "type": "number"
                    },
                    "startPeriodSec": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "args",
                    "user",
                    "timeoutSec",
                    "startPeriodSec"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "healthcheck": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "test": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "intervalSec": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "test",
                    "intervalSec"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "restartPolicy": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "mode": {
                      "type": "string"
                    },
                    "startTimeoutSec": {
                      "type": "number"
                    },
                    "maxRetriesPerNode": {
                      "type": "number"
                    },
                    "maxErrorTolerance": {
                      "type": "number"
                    },
                    "numTasks": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "mode",
                    "startTimeoutSec",
                    "maxRetriesPerNode",
                    "maxErrorTolerance",
                    "numTasks"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ]
            },
            "instances": {
              "type": "object",
              "properties": {
                "min": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                }
              },
              "required": [
                "min",
                "max"
              ],
              "additionalProperties": false
            },
            "stateful": {
              "type": "boolean"
            },
            "hardware": {
              "type": "object",
              "properties": {
                "gpuCount": {
                  "type": "integer"
                },
                "gpuModels": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "gpuManufacturer": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "gpuVramBytes": {
                  "type": "number"
                },
                "ramBytes": {
                  "type": "number"
                },
                "storageBytes": {
                  "type": "number"
                },
                "shmemBytes": {
                  "type": "number"
                }
              },
              "required": [
                "gpuCount",
                "gpuModels",
                "gpuManufacturer",
                "gpuVramBytes",
                "ramBytes",
                "storageBytes",
                "shmemBytes"
              ],
              "additionalProperties": false
            },
            "placement": {
              "type": "object",
              "properties": {
                "regions": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Kinesis regions, e.g. us-west"
                },
                "providers": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "csp": {
                        "type": "string"
                      },
                      "regions": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "region": {
                              "type": "string"
                            },
                            "zones": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            }
                          },
                          "required": [
                            "region",
                            "zones"
                          ],
                          "additionalProperties": false
                        }
                      }
                    },
                    "required": [
                      "csp",
                      "regions"
                    ],
                    "additionalProperties": false
                  }
                },
                "allowedIps": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Source IPs or CIDR ranges allowed to reach the app"
                }
              },
              "required": [
                "regions",
                "providers",
                "allowedIps"
              ],
              "additionalProperties": false
            },
            "grids": {
              "type": "object",
              "properties": {
                "ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "priorities": {
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {
                    "type": "number"
                  },
                  "description": "Priority per grid id"
                }
              },
              "required": [
                "ids",
                "priorities"
              ],
              "additionalProperties": false
            },
            "volumes": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "target": {
                    "type": "string"
                  },
                  "declaredSizeBytes": {
                    "type": "number"
                  }
                },
                "required": [
                  "name",
                  "target",
                  "declaredSizeBytes"
                ],
                "additionalProperties": false
              }
            },
            "domains": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Custom domains attached to the app (see the domain operations)"
            }
          },
          "required": [
            "image",
            "imageTag",
            "registryUsername",
            "ports",
            "env",
            "command",
            "entrypoint",
            "startupCommand",
            "healthcheck",
            "restartPolicy",
            "instances",
            "stateful",
            "hardware",
            "placement",
            "grids",
            "volumes",
            "domains"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ]
    },
    "github": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "installationId": {
              "type": "integer",
              "description": "GitHub App installation the repository is read through"
            },
            "repo": {
              "type": "string",
              "description": "owner/name"
            },
            "url": {
              "type": "string"
            },
            "branch": {
              "type": "string",
              "description": "Branch that is built and, with autoDeploy, followed"
            },
            "contextDir": {
              "type": "string"
            },
            "dockerfilePath": {
              "description": "Dockerfile path relative to contextDir; not used while dockerfileContent is set",
              "type": [
                "string",
                "null"
              ]
            },
            "dockerfileOverride": {
              "type": "boolean",
              "description": "An inline Dockerfile (dockerfileContent) is built instead of dockerfilePath"
            },
            "dockerfileContent": {
              "description": "The inline Dockerfile that is built; null when the repository's own file is used",
              "type": [
                "string",
                "null"
              ]
            },
            "autoDeploy": {
              "type": "boolean",
              "description": "Every push to the branch rebuilds and redeploys"
            },
            "clonedFrom": {
              "description": "Public repository (owner/name) the app's repository was copied from at creation; null otherwise",
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "installationId",
            "repo",
            "url",
            "branch",
            "contextDir",
            "dockerfilePath",
            "dockerfileOverride",
            "dockerfileContent",
            "autoDeploy",
            "clonedFrom"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Repository settings of a GitHub-sourced app"
    },
    "defaultDomain": {
      "description": "Custom domain shown as the app's address in the dashboard; null for the Kinesis-issued one",
      "type": [
        "string",
        "null"
      ]
    },
    "buildId": {
      "description": "Build started by a source change; null when nothing was rebuilt",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "id",
    "projectId",
    "name",
    "guid",
    "source",
    "image",
    "domain",
    "buildState",
    "access",
    "createdBy",
    "createdAt",
    "status",
    "config",
    "github",
    "defaultDomain",
    "buildId"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 app_source_unsupported`, `400 grid_to_serverless`, `400 no_suitable_machines`, `400 serverless_gpu_unavailable`, `429 grid_quota_exceeded`, `403 serverless_disabled`.

## `move_app`

Move an app to another project of its organization. Admins may, and so may the app's creator. Nothing about the app itself changes.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `move_app`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation   | REST                                           | Required scopes |
| ----------- | ---------------------------------------------- | --------------- |
| `apps.move` | `POST /projects/{projectId}/apps/{appId}/move` | `apps:admin`    |

### Arguments

| Argument          | Type     | Required | Description                  |
| ----------------- | -------- | -------- | ---------------------------- |
| `targetProjectId` | `string` | Yes      | Project to move the app into |
| `projectId`       | `string` | Yes      | Project id                   |
| `appId`           | `string` | Yes      | App id                       |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "targetProjectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project to move the app into"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "targetProjectId",
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): App id
* `projectId` (`string`): See the complete response schema below.
* `name` (`string`): See the complete response schema below.
* `guid` (`string`): Runtime identifier of the app
* `source` (`string or null`): Where the image comes from: Docker Hub, a registry link, GitHub or an upload
* `image` (`string or null`): See the complete response schema below.
* `domain` (`string or null`): Hostname the app answers on: the default custom domain, else the Kinesis-issued one
* `buildState` (`string or null`): Build pipeline state for GitHub-sourced apps
* `access` (`object`): See the complete response schema below.
* `createdBy` (`object or null`): Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read
* `createdAt` (`string`): ISO 8601 timestamp (UTC)

<details>

<summary>Response schema: apps.move</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "App id"
    },
    "projectId": {
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "guid": {
      "type": "string",
      "description": "Runtime identifier of the app"
    },
    "source": {
      "description": "Where the image comes from: Docker Hub, a registry link, GitHub or an upload",
      "type": [
        "string",
        "null"
      ]
    },
    "image": {
      "type": [
        "string",
        "null"
      ]
    },
    "domain": {
      "description": "Hostname the app answers on: the default custom domain, else the Kinesis-issued one",
      "type": [
        "string",
        "null"
      ]
    },
    "buildState": {
      "description": "Build pipeline state for GitHub-sourced apps",
      "type": [
        "string",
        "null"
      ]
    },
    "access": {
      "type": "object",
      "properties": {
        "role": {
          "type": "string",
          "enum": [
            "admin",
            "developer",
            "viewer"
          ],
          "description": "The caller's role on it"
        },
        "outside": {
          "type": "boolean",
          "description": "Shared with the caller from another organization"
        },
        "isCreator": {
          "type": "boolean",
          "description": "The caller created it"
        }
      },
      "required": [
        "role",
        "outside",
        "isCreator"
      ],
      "additionalProperties": false
    },
    "createdBy": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "userId": {
              "type": "string",
              "description": "User id, as members.list and the sharing calls use it"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "email": {
              "type": "string"
            }
          },
          "required": [
            "userId",
            "name",
            "email"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    }
  },
  "required": [
    "id",
    "projectId",
    "name",
    "guid",
    "source",
    "image",
    "domain",
    "buildState",
    "access",
    "createdBy",
    "createdAt"
  ],
  "additionalProperties": false
}
```

</details>


# App lifecycle

Inspect desired and actual app state, then start, stop, restart or delete.

Inspect desired and actual app state, then start, stop, restart or delete.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `get_app_status`

Read the live status of an app from the runtime. Aggregate state, each server's container state, where each port answers, recent errors. Poll this after start, stop or delete.

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `get_app_status`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation     | REST                                            | Required scopes |
| ------------- | ----------------------------------------------- | --------------- |
| `apps.status` | `GET /projects/{projectId}/apps/{appId}/status` | `apps:read`     |

### Arguments

| Argument    | Type     | Required | Description |
| ----------- | -------- | -------- | ----------- |
| `projectId` | `string` | Yes      | Project id  |
| `appId`     | `string` | Yes      | App id      |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `state` (`"unknown" / "running" / "stopped" / "starting" / "stopping" / "scaling" / "updating" / "downloading" / "provisioning" / "start_blocked" / "error" / "building" / "build_failed"`): Aggregate state; build states win while there is no runnable image
* `targetState` (`"running" / "stopped" / "unknown"`): What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds
* `nodes` (`array<object>`): Servers the app is placed on and the container state on each
* `endpoints` (`array<object>`): Where each configured port answers; a load-balanced port gets its address a minute or two after the first start
* `errors` (`array<object>`): Recent runtime errors, newest first
* `workOrders` (`array<object>`): The runtime's latest instructions to servers, newest first; an order that never completes is the stall
* `grid` (`object or null`): Grid placement details; null for a serverless app
* `pollAfterSec` (`integer or null`): Seconds to wait before reading the status again while it is changing; null in a settled state
* `id` (`string`): App id

<details>

<summary>Response schema: apps.status</summary>

```json
{
  "type": "object",
  "properties": {
    "state": {
      "type": "string",
      "enum": [
        "unknown",
        "running",
        "stopped",
        "starting",
        "stopping",
        "scaling",
        "updating",
        "downloading",
        "provisioning",
        "start_blocked",
        "error",
        "building",
        "build_failed"
      ],
      "description": "Aggregate state; build states win while there is no runnable image"
    },
    "targetState": {
      "type": "string",
      "enum": [
        "running",
        "stopped",
        "unknown"
      ],
      "description": "What the runtime was last asked for; unknown until it has been asked, as for a repository app before its first build succeeds"
    },
    "nodes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "reportedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ],
            "description": "When the server last reported this state; not a start time"
          }
        },
        "required": [
          "address",
          "state",
          "reportedAt"
        ],
        "additionalProperties": false
      },
      "description": "Servers the app is placed on and the container state on each"
    },
    "endpoints": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "port": {
            "type": "integer",
            "description": "Container port, as in config.ports"
          },
          "protocol": {
            "type": "string",
            "description": "Protocol of that port, as in config.ports"
          },
          "address": {
            "description": "An http(s) URL for the HTTP protocols, host:port for the others; null until the endpoint is wired",
            "type": [
              "string",
              "null"
            ]
          },
          "server": {
            "description": "Server address of a direct tcp or udp endpoint; null when load-balanced",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "port",
          "protocol",
          "address",
          "server"
        ],
        "additionalProperties": false
      },
      "description": "Where each configured port answers; a load-balanced port gets its address a minute or two after the first start"
    },
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "details": {
            "anyOf": [
              {
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {}
              },
              {
                "type": "null"
              }
            ],
            "description": "Extra data the runtime attached, such as the grid or server involved"
          }
        },
        "required": [
          "type",
          "message",
          "at",
          "details"
        ],
        "additionalProperties": false
      },
      "description": "Recent runtime errors, newest first"
    },
    "workOrders": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "What the runtime asked the server to do"
          },
          "server": {
            "type": "string",
            "description": "Server address"
          },
          "status": {
            "type": "string"
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "startedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "completedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "type",
          "server",
          "status",
          "createdAt",
          "startedAt",
          "completedAt"
        ],
        "additionalProperties": false
      },
      "description": "The runtime's latest instructions to servers, newest first; an order that never completes is the stall"
    },
    "grid": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "fill": {
              "anyOf": [
                {
                  "type": "object",
                  "properties": {
                    "target": {
                      "type": "integer",
                      "description": "Members the grid is composed to have"
                    },
                    "online": {
                      "type": "integer",
                      "description": "Members the app runs on"
                    },
                    "pending": {
                      "type": "integer"
                    },
                    "converging": {
                      "type": "boolean",
                      "description": "The grid is still acquiring members or placing the app"
                    }
                  },
                  "required": [
                    "target",
                    "online",
                    "pending",
                    "converging"
                  ],
                  "additionalProperties": false
                },
                {
                  "type": "null"
                }
              ],
              "description": "How far the grid has filled for this app"
            },
            "idleReclaim": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "gridId": {
                    "type": "string"
                  },
                  "warnedAt": {
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "date-time",
                        "description": "ISO 8601 timestamp (UTC)"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "evictAt": {
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "date-time",
                        "description": "ISO 8601 timestamp (UTC)"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "When the app becomes evictable; null when the grid only warns"
                  },
                  "evictedAt": {
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "date-time",
                        "description": "ISO 8601 timestamp (UTC)"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "cooldownEndsAt": {
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "date-time",
                        "description": "ISO 8601 timestamp (UTC)"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "When the grid takes the app again after an eviction"
                  },
                  "evidence": {
                    "type": "string",
                    "description": "Why the grid considers the app idle"
                  }
                },
                "required": [
                  "gridId",
                  "warnedAt",
                  "evictAt",
                  "evictedAt",
                  "cooldownEndsAt",
                  "evidence"
                ],
                "additionalProperties": false
              },
              "description": "Grids whose idle policy has warned or evicted the app"
            },
            "queue": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "gridId": {
                    "type": "string"
                  },
                  "position": {
                    "type": "integer",
                    "description": "1-based place in the grid's fair-share queue"
                  },
                  "queueLength": {
                    "type": "integer"
                  },
                  "enqueuedAt": {
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "date-time",
                        "description": "ISO 8601 timestamp (UTC)"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "held": {
                    "type": "boolean",
                    "description": "The scheduler keeps the app off the grid until its turn comes"
                  }
                },
                "required": [
                  "gridId",
                  "position",
                  "queueLength",
                  "enqueuedAt",
                  "held"
                ],
                "additionalProperties": false
              },
              "description": "Full grids the app is queued on"
            }
          },
          "required": [
            "fill",
            "idleReclaim",
            "queue"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Grid placement details; null for a serverless app"
    },
    "pollAfterSec": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "description": "Seconds to wait before reading the status again while it is changing; null in a settled state"
    },
    "id": {
      "type": "string",
      "description": "App id"
    }
  },
  "required": [
    "state",
    "targetState",
    "nodes",
    "endpoints",
    "errors",
    "workOrders",
    "grid",
    "pollAfterSec",
    "id"
  ],
  "additionalProperties": false
}
```

</details>

## `start_app`

Ask the runtime to start an app. Returns once the request is accepted; poll the status to follow it.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `start_app`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: true`

### Permissions and REST equivalents

| Operation    | REST                                            | Required scopes               |
| ------------ | ----------------------------------------------- | ----------------------------- |
| `apps.start` | `POST /projects/{projectId}/apps/{appId}/start` | `apps:write`, `compute:spend` |

### Arguments

| Argument    | Type     | Required | Description |
| ----------- | -------- | -------- | ----------- |
| `projectId` | `string` | Yes      | Project id  |
| `appId`     | `string` | Yes      | App id      |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): App id
* `targetState` (`"running" / "stopped"`): State the runtime is now converging on

<details>

<summary>Response schema: apps.start</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "App id"
    },
    "targetState": {
      "type": "string",
      "enum": [
        "running",
        "stopped"
      ],
      "description": "State the runtime is now converging on"
    }
  },
  "required": [
    "id",
    "targetState"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `402 balance_over_limit`, `429 grid_quota_exceeded`, `403 serverless_disabled`.

## `stop_app`

Ask the runtime to stop an app. Returns once the request is accepted; poll the status to follow it.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `stop_app`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation   | REST                                           | Required scopes |
| ----------- | ---------------------------------------------- | --------------- |
| `apps.stop` | `POST /projects/{projectId}/apps/{appId}/stop` | `apps:write`    |

### Arguments

| Argument    | Type     | Required | Description |
| ----------- | -------- | -------- | ----------- |
| `projectId` | `string` | Yes      | Project id  |
| `appId`     | `string` | Yes      | App id      |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): App id
* `targetState` (`"running" / "stopped"`): State the runtime is now converging on

<details>

<summary>Response schema: apps.stop</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "App id"
    },
    "targetState": {
      "type": "string",
      "enum": [
        "running",
        "stopped"
      ],
      "description": "State the runtime is now converging on"
    }
  },
  "required": [
    "id",
    "targetState"
  ],
  "additionalProperties": false
}
```

</details>

## `restart_app`

Stop an app, wait for it to report stopped, then start it again. It waits up to waitSec (default 30); if the app is still stopping when the wait runs out the start is issued anyway; check get\_app\_status afterwards.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `restart_app`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true`

### Permissions and REST equivalents

| Operation      | REST                                              | Required scopes               |
| -------------- | ------------------------------------------------- | ----------------------------- |
| `apps.restart` | `POST /projects/{projectId}/apps/{appId}/restart` | `apps:write`, `compute:spend` |

### Arguments

| Argument    | Type      | Required | Description                                                         |
| ----------- | --------- | -------- | ------------------------------------------------------------------- |
| `waitSec`   | `integer` | No       | Seconds to wait for the stop before starting, 0 to 180 (default 30) |
| `projectId` | `string`  | Yes      | Project id                                                          |
| `appId`     | `string`  | Yes      | App id                                                              |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "waitSec": {
      "type": "integer",
      "minimum": 0,
      "maximum": 180,
      "description": "Seconds to wait for the stop before starting, 0 to 180 (default 30)"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): App id
* `targetState` (`"running"`): State the runtime is now converging on
* `stoppedBeforeStart` (`boolean`): The app reported stopped before the start was issued; false when the wait ran out
* `waitedSec` (`integer`): Seconds spent waiting for the stop

<details>

<summary>Response schema: apps.restart</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "App id"
    },
    "targetState": {
      "type": "string",
      "const": "running",
      "description": "State the runtime is now converging on"
    },
    "stoppedBeforeStart": {
      "type": "boolean",
      "description": "The app reported stopped before the start was issued; false when the wait ran out"
    },
    "waitedSec": {
      "type": "integer",
      "description": "Seconds spent waiting for the stop"
    }
  },
  "required": [
    "id",
    "targetState",
    "stoppedBeforeStart",
    "waitedSec"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `402 balance_over_limit`, `429 grid_quota_exceeded`, `403 serverless_disabled`.

## `delete_app`

Delete a stopped app (admins, or its creator). Fails with app\_running while any node is still live. Irreversible.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `delete_app`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

Requires `confirm: true` for this irreversible action.

### Permissions and REST equivalents

| Operation     | REST                                        | Required scopes |
| ------------- | ------------------------------------------- | --------------- |
| `apps.delete` | `DELETE /projects/{projectId}/apps/{appId}` | `apps:admin`    |

### Arguments

| Argument    | Type     | Required | Description                                |
| ----------- | -------- | -------- | ------------------------------------------ |
| `projectId` | `string` | Yes      | Project id                                 |
| `appId`     | `string` | Yes      | App id                                     |
| `confirm`   | `true`   | Yes      | Must be true: this action cannot be undone |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "confirm": {
      "type": "boolean",
      "const": true,
      "description": "Must be true: this action cannot be undone"
    }
  },
  "required": [
    "projectId",
    "appId",
    "confirm"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Id of the deleted app
* `deleted` (`true`): See the complete response schema below.

<details>

<summary>Response schema: apps.delete</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Id of the deleted app"
    },
    "deleted": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "id",
    "deleted"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 app_running`.


# Logs and usage

Read per-server runtime logs and resource usage over time.

Read per-server runtime logs and resource usage over time.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

Logs and log exports contain the app’s raw output and can include sensitive data. The OpenAI profile’s environment-value filtering does not sanitize log text.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `get_app_logs`

Read a page of container logs for an app. Paging starts at the newest lines and moves back in time by default; inside a page, chunks and lines run oldest to newest, so the last line is the most recent. Lines are the app's own output, as it wrote them, so they can hold anything it prints, sensitive data included.

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `get_app_logs`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation   | REST                                          | Required scopes   |
| ----------- | --------------------------------------------- | ----------------- |
| `apps.logs` | `GET /projects/{projectId}/apps/{appId}/logs` | `monitoring:read` |

### Arguments

| Argument    | Type                | Required | Description                                                                                                               |
| ----------- | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `projectId` | `string`            | Yes      | Project id                                                                                                                |
| `appId`     | `string`            | Yes      | App id                                                                                                                    |
| `nodes`     | `array<string>`     | No       | Server addresses to read from; omit for every server the app ran on                                                       |
| `cursorNs`  | `string`            | No       | Cursor from a previous page (nextCursorNs)                                                                                |
| `cursor`    | `integer`           | No       | Unix time in milliseconds to seek to when no cursorNs is given; 0 or omitted means now                                    |
| `pageSize`  | `integer`           | No       | Lines per page, at most 1000 (default 200)                                                                                |
| `direction` | `"older" / "newer"` | No       | Which way the next page moves from the cursor: older (the default) or newer. Lines inside a page are always chronological |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "nodes": {
      "maxItems": 50,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "description": "Server addresses to read from; omit for every server the app ran on"
    },
    "cursorNs": {
      "type": "string",
      "description": "Cursor from a previous page (nextCursorNs)"
    },
    "cursor": {
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991,
      "description": "Unix time in milliseconds to seek to when no cursorNs is given; 0 or omitted means now"
    },
    "pageSize": {
      "type": "integer",
      "minimum": 1,
      "maximum": 1000,
      "description": "Lines per page, at most 1000 (default 200)"
    },
    "direction": {
      "type": "string",
      "enum": [
        "older",
        "newer"
      ],
      "description": "Which way the next page moves from the cursor: older (the default) or newer. Lines inside a page are always chronological"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `logs` (`array<object>`): Chunks in chronological order, consecutive lines from one server merged into a chunk that from and to span; the last line of the last chunk is the newest
* `nextCursorNs` (`string or null`): Pass as cursorNs to fetch the next page in the same direction; null at the end
* `hasMore` (`boolean`): See the complete response schema below.
* `totalLines` (`integer`): Lines in this page across every chunk

<details>

<summary>Response schema: apps.logs</summary>

```json
{
  "type": "object",
  "properties": {
    "logs": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "node": {
            "type": "string",
            "description": "Server address the lines came from"
          },
          "from": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "to": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "node",
          "from",
          "to",
          "lines"
        ],
        "additionalProperties": false
      },
      "description": "Chunks in chronological order, consecutive lines from one server merged into a chunk that from and to span; the last line of the last chunk is the newest"
    },
    "nextCursorNs": {
      "description": "Pass as cursorNs to fetch the next page in the same direction; null at the end",
      "type": [
        "string",
        "null"
      ]
    },
    "hasMore": {
      "type": "boolean"
    },
    "totalLines": {
      "type": "integer",
      "description": "Lines in this page across every chunk"
    }
  },
  "required": [
    "logs",
    "nextCursorNs",
    "hasMore",
    "totalLines"
  ],
  "additionalProperties": false
}
```

</details>

## `get_app_usage`

Resource usage of an app over a time range, bucketed per server

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `get_app_usage`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation    | REST                                           | Required scopes   |
| ------------ | ---------------------------------------------- | ----------------- |
| `apps.usage` | `GET /projects/{projectId}/apps/{appId}/usage` | `monitoring:read` |

### Arguments

| Argument    | Type     | Required | Description                                                        |
| ----------- | -------- | -------- | ------------------------------------------------------------------ |
| `projectId` | `string` | Yes      | Project id                                                         |
| `appId`     | `string` | Yes      | App id                                                             |
| `start`     | `string` | No       | Range start (ISO 8601); defaults to one hour before end            |
| `end`       | `string` | No       | Range end (ISO 8601); defaults to now. At most 30 days after start |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "start": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
      "description": "Range start (ISO 8601); defaults to one hour before end"
    },
    "end": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
      "description": "Range end (ISO 8601); defaults to now. At most 30 days after start"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `start` (`string`): ISO 8601 timestamp (UTC)
* `end` (`string`): ISO 8601 timestamp (UTC)
* `intervalSeconds` (`integer`): Width of each bucket
* `series` (`array<object>`): One series per server the app ran on; each point sums the bucket

<details>

<summary>Response schema: apps.usage</summary>

```json
{
  "type": "object",
  "properties": {
    "start": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    },
    "end": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    },
    "intervalSeconds": {
      "type": "integer",
      "description": "Width of each bucket"
    },
    "series": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "node": {
            "type": "string",
            "description": "Server address"
          },
          "points": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Bucket start"
                },
                "cpuNs": {
                  "type": "number",
                  "description": "CPU time consumed, in nanoseconds"
                },
                "gpuNs": {
                  "type": "number",
                  "description": "GPU time consumed, in nanoseconds"
                },
                "gpuMemBytesSecs": {
                  "type": "number",
                  "description": "GPU memory held, in byte-seconds"
                },
                "memBytesSecs": {
                  "type": "number",
                  "description": "RAM held, in byte-seconds"
                },
                "storageBytesSecs": {
                  "type": "number",
                  "description": "Disk held, in byte-seconds"
                },
                "logStorageBytesSecs": {
                  "type": "number",
                  "description": "Log storage held, in byte-seconds"
                },
                "bwInBytes": {
                  "type": "number",
                  "description": "Bytes received"
                },
                "bwOutBytes": {
                  "type": "number",
                  "description": "Bytes sent"
                }
              },
              "required": [
                "at",
                "cpuNs",
                "gpuNs",
                "gpuMemBytesSecs",
                "memBytesSecs",
                "storageBytesSecs",
                "logStorageBytesSecs",
                "bwInBytes",
                "bwOutBytes"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "node",
          "points"
        ],
        "additionalProperties": false
      },
      "description": "One series per server the app ran on; each point sums the bucket"
    }
  },
  "required": [
    "start",
    "end",
    "intervalSeconds",
    "series"
  ],
  "additionalProperties": false
}
```

</details>

## `export_logs`

The app's container logs as one text, oldest first. Every line is stamped with its time and server, up to maxBytes (1 MiB by default, 4 MiB at most). For a page at a time use apps.logs instead. Lines are the app's own output, as it wrote them, so they can hold anything it prints, sensitive data included.

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `export_logs`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation          | REST                                                 | Required scopes   |
| ------------------ | ---------------------------------------------------- | ----------------- |
| `apps.logs.export` | `GET /projects/{projectId}/apps/{appId}/logs/export` | `monitoring:read` |

### Arguments

| Argument    | Type            | Required | Description                                            |
| ----------- | --------------- | -------- | ------------------------------------------------------ |
| `projectId` | `string`        | Yes      | Project id                                             |
| `appId`     | `string`        | Yes      | App id                                                 |
| `nodes`     | `array<string>` | No       | Server addresses to include; omit for all              |
| `maxBytes`  | `integer`       | No       | Stop after this many bytes of text; 1048576 by default |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "nodes": {
      "maxItems": 50,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "description": "Server addresses to include; omit for all"
    },
    "maxBytes": {
      "type": "integer",
      "minimum": 1024,
      "maximum": 4194304,
      "description": "Stop after this many bytes of text; 1048576 by default"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `text` (`string`): The log lines, newline-separated
* `lines` (`integer`): See the complete response schema below.
* `truncated` (`boolean`): maxBytes was reached before the end; the newest lines are missing

<details>

<summary>Response schema: apps.logs.export</summary>

```json
{
  "type": "object",
  "properties": {
    "text": {
      "type": "string",
      "description": "The log lines, newline-separated"
    },
    "lines": {
      "type": "integer"
    },
    "truncated": {
      "type": "boolean",
      "description": "maxBytes was reached before the end; the newest lines are missing"
    }
  },
  "required": [
    "text",
    "lines",
    "truncated"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `429 too_many_attempts`.

## `get_log_retention`

How long the app's container logs are kept

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `get_log_retention`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                 | REST                                                    | Required scopes |
| ------------------------- | ------------------------------------------------------- | --------------- |
| `apps.logs.retention.get` | `GET /projects/{projectId}/apps/{appId}/logs/retention` | `apps:read`     |

### Arguments

| Argument    | Type     | Required | Description |
| ----------- | -------- | -------- | ----------- |
| `projectId` | `string` | Yes      | Project id  |
| `appId`     | `string` | Yes      | App id      |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `days` (`integer`): How many days of container logs are kept
* `isDefault` (`boolean`): The platform default applies; no per-app setting

<details>

<summary>Response schema: apps.logs.retention.get</summary>

```json
{
  "type": "object",
  "properties": {
    "days": {
      "type": "integer",
      "description": "How many days of container logs are kept"
    },
    "isDefault": {
      "type": "boolean",
      "description": "The platform default applies; no per-app setting"
    }
  },
  "required": [
    "days",
    "isDefault"
  ],
  "additionalProperties": false
}
```

</details>

## `set_log_retention`

Set how many days the app's container logs are kept. 1 to 180; 0 goes back to the platform default

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `set_log_retention`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                    | REST                                                      | Required scopes |
| ---------------------------- | --------------------------------------------------------- | --------------- |
| `apps.logs.retention.update` | `PATCH /projects/{projectId}/apps/{appId}/logs/retention` | `apps:write`    |

### Arguments

| Argument    | Type      | Required | Description                     |
| ----------- | --------- | -------- | ------------------------------- |
| `days`      | `integer` | Yes      | Days to keep; 0 for the default |
| `projectId` | `string`  | Yes      | Project id                      |
| `appId`     | `string`  | Yes      | App id                          |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "days": {
      "type": "integer",
      "minimum": 0,
      "maximum": 180,
      "description": "Days to keep; 0 for the default"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "days",
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `days` (`integer`): How many days of container logs are kept
* `isDefault` (`boolean`): The platform default applies; no per-app setting

<details>

<summary>Response schema: apps.logs.retention.update</summary>

```json
{
  "type": "object",
  "properties": {
    "days": {
      "type": "integer",
      "description": "How many days of container logs are kept"
    },
    "isDefault": {
      "type": "boolean",
      "description": "The platform default applies; no per-app setting"
    }
  },
  "required": [
    "days",
    "isDefault"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `429 too_many_attempts`.

## `clear_logs`

Delete the app's container logs older than a point in time. Cannot be undone.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `clear_logs`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

Requires `confirm: true` for this irreversible action.

### Permissions and REST equivalents

| Operation         | REST                                                 | Required scopes |
| ----------------- | ---------------------------------------------------- | --------------- |
| `apps.logs.clear` | `POST /projects/{projectId}/apps/{appId}/logs/clear` | `apps:write`    |

### Arguments

| Argument    | Type     | Required | Description                                |
| ----------- | -------- | -------- | ------------------------------------------ |
| `olderThan` | `string` | Yes      | Lines before this instant are deleted      |
| `projectId` | `string` | Yes      | Project id                                 |
| `appId`     | `string` | Yes      | App id                                     |
| `confirm`   | `true`   | Yes      | Must be true: this action cannot be undone |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "olderThan": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
      "description": "Lines before this instant are deleted"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "confirm": {
      "type": "boolean",
      "const": true,
      "description": "Must be true: this action cannot be undone"
    }
  },
  "required": [
    "olderThan",
    "projectId",
    "appId",
    "confirm"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `deletedCount` (`integer`): Lines deleted

<details>

<summary>Response schema: apps.logs.clear</summary>

```json
{
  "type": "object",
  "properties": {
    "deletedCount": {
      "type": "integer",
      "description": "Lines deleted"
    }
  },
  "required": [
    "deletedCount"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `429 too_many_attempts`.


# Builds

Follow GitHub builds, inspect output, trigger deployments and roll back to a stored image.

Follow GitHub builds, inspect output, trigger deployments and roll back to a stored image.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

`check_rollback_build` checks whether an earlier image is still stored. `rollback_build` redeploys that image without rebuilding: a running app redeploys, a stopped app uses it on its next start. It requires `apps:write` and permission to edit the app. It is refused during an active build, when no image remains, or before the app has ever deployed. The next build replaces the rollback. Build output is raw and may contain sensitive data.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `list_builds`

List the builds of a GitHub-sourced app, newest first

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `list_builds`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation          | REST                                            | Required scopes |
| ------------------ | ----------------------------------------------- | --------------- |
| `apps.builds.list` | `GET /projects/{projectId}/apps/{appId}/builds` | `apps:read`     |

### Arguments

| Argument    | Type      | Required | Description                                                |
| ----------- | --------- | -------- | ---------------------------------------------------------- |
| `projectId` | `string`  | Yes      | Project id                                                 |
| `appId`     | `string`  | Yes      | App id                                                     |
| `limit`     | `integer` | No       | Items per page, 1 to 200 (default 50)                      |
| `cursor`    | `string`  | No       | nextCursor from the previous page; omit for the first page |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 200,
      "description": "Items per page, 1 to 200 (default 50)"
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "nextCursor from the previous page; omit for the first page"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `builds` (`array<object>`): See the complete response schema below.
* `nextCursor` (`string or null`): Pass as cursor to fetch the next page; null on the last page

<details>

<summary>Response schema: apps.builds.list</summary>

```json
{
  "type": "object",
  "properties": {
    "builds": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Build id"
          },
          "appId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "succeeded",
              "failed",
              "cancelled"
            ],
            "description": "running until the image is built and the app deployed"
          },
          "step": {
            "type": "string",
            "enum": [
              "vcs_import",
              "image_build",
              "app_deploy",
              "completed"
            ],
            "description": "Pipeline step in progress, or completed"
          },
          "imageBuildStatus": {
            "description": "The image builder's own status (Created, Running, Completed, Failed, Canceled, TimedOut) once that step started",
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "repo": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "branch": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "commitSha": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "commitMessage": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "contextDir": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "dockerfilePath": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "repo",
                  "branch",
                  "commitSha",
                  "commitMessage",
                  "contextDir",
                  "dockerfilePath"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "What was built; the commit is filled in once the build starts"
          },
          "error": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "step": {
                    "type": "string",
                    "description": "Step the build failed at"
                  },
                  "message": {
                    "type": "string"
                  },
                  "code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "step",
                  "message",
                  "code"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          },
          "startedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "finishedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "pollAfterSec": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Seconds to wait before reading the build again; null once it has finished"
          }
        },
        "required": [
          "id",
          "appId",
          "status",
          "step",
          "imageBuildStatus",
          "source",
          "error",
          "createdAt",
          "startedAt",
          "finishedAt",
          "pollAfterSec"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": {
      "description": "Pass as cursor to fetch the next page; null on the last page",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "builds",
    "nextCursor"
  ],
  "additionalProperties": false
}
```

</details>

## `get_build`

Get one build: its status, pipeline step, source commit and error

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `get_build`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation         | REST                                                      | Required scopes |
| ----------------- | --------------------------------------------------------- | --------------- |
| `apps.builds.get` | `GET /projects/{projectId}/apps/{appId}/builds/{buildId}` | `apps:read`     |

### Arguments

| Argument    | Type     | Required | Description |
| ----------- | -------- | -------- | ----------- |
| `projectId` | `string` | Yes      | Project id  |
| `appId`     | `string` | Yes      | App id      |
| `buildId`   | `string` | Yes      | Build id    |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "buildId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Build id"
    }
  },
  "required": [
    "projectId",
    "appId",
    "buildId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Build id
* `appId` (`string`): See the complete response schema below.
* `status` (`"pending" / "running" / "succeeded" / "failed" / "cancelled"`): running until the image is built and the app deployed
* `step` (`"vcs_import" / "image_build" / "app_deploy" / "completed"`): Pipeline step in progress, or completed
* `imageBuildStatus` (`string or null`): The image builder's own status (Created, Running, Completed, Failed, Canceled, TimedOut) once that step started
* `source` (`object or null`): What was built; the commit is filled in once the build starts
* `error` (`object or null`): See the complete response schema below.
* `createdAt` (`string`): ISO 8601 timestamp (UTC)
* `startedAt` (`string or null`): See the complete response schema below.
* `finishedAt` (`string or null`): See the complete response schema below.
* `pollAfterSec` (`integer or null`): Seconds to wait before reading the build again; null once it has finished

<details>

<summary>Response schema: apps.builds.get</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Build id"
    },
    "appId": {
      "type": "string"
    },
    "status": {
      "type": "string",
      "enum": [
        "pending",
        "running",
        "succeeded",
        "failed",
        "cancelled"
      ],
      "description": "running until the image is built and the app deployed"
    },
    "step": {
      "type": "string",
      "enum": [
        "vcs_import",
        "image_build",
        "app_deploy",
        "completed"
      ],
      "description": "Pipeline step in progress, or completed"
    },
    "imageBuildStatus": {
      "description": "The image builder's own status (Created, Running, Completed, Failed, Canceled, TimedOut) once that step started",
      "type": [
        "string",
        "null"
      ]
    },
    "source": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "repo": {
              "type": [
                "string",
                "null"
              ]
            },
            "branch": {
              "type": [
                "string",
                "null"
              ]
            },
            "commitSha": {
              "type": [
                "string",
                "null"
              ]
            },
            "commitMessage": {
              "type": [
                "string",
                "null"
              ]
            },
            "contextDir": {
              "type": [
                "string",
                "null"
              ]
            },
            "dockerfilePath": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "repo",
            "branch",
            "commitSha",
            "commitMessage",
            "contextDir",
            "dockerfilePath"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "What was built; the commit is filled in once the build starts"
    },
    "error": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "step": {
              "type": "string",
              "description": "Step the build failed at"
            },
            "message": {
              "type": "string"
            },
            "code": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "required": [
            "step",
            "message",
            "code"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ]
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    },
    "startedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ]
    },
    "finishedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ]
    },
    "pollAfterSec": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "description": "Seconds to wait before reading the build again; null once it has finished"
    }
  },
  "required": [
    "id",
    "appId",
    "status",
    "step",
    "imageBuildStatus",
    "source",
    "error",
    "createdAt",
    "startedAt",
    "finishedAt",
    "pollAfterSec"
  ],
  "additionalProperties": false
}
```

</details>

## `get_build_logs`

Read the image builder's log for a build. The whole log once the build has finished, a snapshot of what has been written so far while it runs (empty until the builder starts writing). It includes whatever the repository's build steps print, sensitive data included.

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `get_build_logs`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation          | REST                                                           | Required scopes   |
| ------------------ | -------------------------------------------------------------- | ----------------- |
| `apps.builds.logs` | `GET /projects/{projectId}/apps/{appId}/builds/{buildId}/logs` | `monitoring:read` |

### Arguments

| Argument    | Type     | Required | Description |
| ----------- | -------- | -------- | ----------- |
| `projectId` | `string` | Yes      | Project id  |
| `appId`     | `string` | Yes      | App id      |
| `buildId`   | `string` | Yes      | Build id    |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "buildId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Build id"
    }
  },
  "required": [
    "projectId",
    "appId",
    "buildId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Build id
* `status` (`"pending" / "running" / "succeeded" / "failed" / "cancelled"`): See the complete response schema below.
* `imageBuildStatus` (`string or null`): See the complete response schema below.
* `lines` (`array<string>`): Builder output, oldest first
* `complete` (`boolean`): The image build has finished, so the log is final
* `truncated` (`boolean`): Older lines were dropped to keep the last 2000

<details>

<summary>Response schema: apps.builds.logs</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Build id"
    },
    "status": {
      "type": "string",
      "enum": [
        "pending",
        "running",
        "succeeded",
        "failed",
        "cancelled"
      ]
    },
    "imageBuildStatus": {
      "type": [
        "string",
        "null"
      ]
    },
    "lines": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Builder output, oldest first"
    },
    "complete": {
      "type": "boolean",
      "description": "The image build has finished, so the log is final"
    },
    "truncated": {
      "type": "boolean",
      "description": "Older lines were dropped to keep the last 2000"
    }
  },
  "required": [
    "id",
    "status",
    "imageBuildStatus",
    "lines",
    "complete",
    "truncated"
  ],
  "additionalProperties": false
}
```

</details>

## `trigger_build`

Rebuild a GitHub-sourced app and redeploy it when the build succeeds. From its branch head or one commit; a build still running is cancelled first. Poll get\_build to follow it.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `trigger_build`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true`

### Permissions and REST equivalents

| Operation            | REST                                             | Required scopes               |
| -------------------- | ------------------------------------------------ | ----------------------------- |
| `apps.builds.create` | `POST /projects/{projectId}/apps/{appId}/builds` | `apps:write`, `compute:spend` |

### Arguments

| Argument    | Type     | Required | Description                                |
| ----------- | -------- | -------- | ------------------------------------------ |
| `commitSha` | `string` | No       | Commit to build instead of the branch head |
| `projectId` | `string` | Yes      | Project id                                 |
| `appId`     | `string` | Yes      | App id                                     |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "commitSha": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{7,40}$",
      "description": "Commit to build instead of the branch head"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    }
  },
  "required": [
    "projectId",
    "appId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `build` (`object`): See the complete response schema below.
* `cancelledBuilds` (`integer`): Builds that were still running and were cancelled

<details>

<summary>Response schema: apps.builds.create</summary>

```json
{
  "type": "object",
  "properties": {
    "build": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Build id"
        },
        "appId": {
          "type": "string"
        },
        "status": {
          "type": "string",
          "enum": [
            "pending",
            "running",
            "succeeded",
            "failed",
            "cancelled"
          ],
          "description": "running until the image is built and the app deployed"
        },
        "step": {
          "type": "string",
          "enum": [
            "vcs_import",
            "image_build",
            "app_deploy",
            "completed"
          ],
          "description": "Pipeline step in progress, or completed"
        },
        "imageBuildStatus": {
          "description": "The image builder's own status (Created, Running, Completed, Failed, Canceled, TimedOut) once that step started",
          "type": [
            "string",
            "null"
          ]
        },
        "source": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "repo": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "branch": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "commitSha": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "commitMessage": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "contextDir": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "dockerfilePath": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "repo",
                "branch",
                "commitSha",
                "commitMessage",
                "contextDir",
                "dockerfilePath"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "What was built; the commit is filled in once the build starts"
        },
        "error": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "step": {
                  "type": "string",
                  "description": "Step the build failed at"
                },
                "message": {
                  "type": "string"
                },
                "code": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "step",
                "message",
                "code"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ]
        },
        "createdAt": {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        "startedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 timestamp (UTC)"
            },
            {
              "type": "null"
            }
          ]
        },
        "finishedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 timestamp (UTC)"
            },
            {
              "type": "null"
            }
          ]
        },
        "pollAfterSec": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "description": "Seconds to wait before reading the build again; null once it has finished"
        }
      },
      "required": [
        "id",
        "appId",
        "status",
        "step",
        "imageBuildStatus",
        "source",
        "error",
        "createdAt",
        "startedAt",
        "finishedAt",
        "pollAfterSec"
      ],
      "additionalProperties": false
    },
    "cancelledBuilds": {
      "type": "integer",
      "description": "Builds that were still running and were cancelled"
    }
  },
  "required": [
    "build",
    "cancelledBuilds"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 app_source_unsupported`, `403 serverless_disabled`, `503 github_not_configured`, `404 github_installation_not_found`, `404 github_repo_not_found`, `502 upstream_github`, `404 github_commit_not_found`.

## `check_rollback_build`

Whether an earlier build's image can still be redeployed with rollback\_build. False when the build has no image (it failed or was cancelled) or its image is no longer stored.

**Group:** `apps` · **Behavior:** Read

**Compact tool:** `apps` · **Action:** `check_rollback_build`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                   | REST                                                               | Required scopes |
| --------------------------- | ------------------------------------------------------------------ | --------------- |
| `apps.builds.rollbackCheck` | `GET /projects/{projectId}/apps/{appId}/builds/{buildId}/rollback` | `apps:read`     |

### Arguments

| Argument    | Type     | Required | Description |
| ----------- | -------- | -------- | ----------- |
| `projectId` | `string` | Yes      | Project id  |
| `appId`     | `string` | Yes      | App id      |
| `buildId`   | `string` | Yes      | Build id    |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "buildId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Build id"
    }
  },
  "required": [
    "projectId",
    "appId",
    "buildId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `imageAvailable` (`boolean`): The build's image can be redeployed

<details>

<summary>Response schema: apps.builds.rollbackCheck</summary>

```json
{
  "type": "object",
  "properties": {
    "imageAvailable": {
      "type": "boolean",
      "description": "The build's image can be redeployed"
    }
  },
  "required": [
    "imageAvailable"
  ],
  "additionalProperties": false
}
```

</details>

## `rollback_build`

Redeploy an earlier build's image without rebuilding. A running app is redeployed with it; the next build replaces it again.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `rollback_build`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: true`

### Permissions and REST equivalents

| Operation              | REST                                                                | Required scopes |
| ---------------------- | ------------------------------------------------------------------- | --------------- |
| `apps.builds.rollback` | `POST /projects/{projectId}/apps/{appId}/builds/{buildId}/rollback` | `apps:write`    |

### Arguments

| Argument    | Type     | Required | Description |
| ----------- | -------- | -------- | ----------- |
| `projectId` | `string` | Yes      | Project id  |
| `appId`     | `string` | Yes      | App id      |
| `buildId`   | `string` | Yes      | Build id    |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "buildId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Build id"
    }
  },
  "required": [
    "projectId",
    "appId",
    "buildId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `build` (`object`): The build whose image is now deployed
* `redeploying` (`boolean`): The app is running and is being redeployed with the image

<details>

<summary>Response schema: apps.builds.rollback</summary>

```json
{
  "type": "object",
  "properties": {
    "build": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "Build id"
        },
        "appId": {
          "type": "string"
        },
        "status": {
          "type": "string",
          "enum": [
            "pending",
            "running",
            "succeeded",
            "failed",
            "cancelled"
          ],
          "description": "running until the image is built and the app deployed"
        },
        "step": {
          "type": "string",
          "enum": [
            "vcs_import",
            "image_build",
            "app_deploy",
            "completed"
          ],
          "description": "Pipeline step in progress, or completed"
        },
        "imageBuildStatus": {
          "description": "The image builder's own status (Created, Running, Completed, Failed, Canceled, TimedOut) once that step started",
          "type": [
            "string",
            "null"
          ]
        },
        "source": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "repo": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "branch": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "commitSha": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "commitMessage": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "contextDir": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "dockerfilePath": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "repo",
                "branch",
                "commitSha",
                "commitMessage",
                "contextDir",
                "dockerfilePath"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ],
          "description": "What was built; the commit is filled in once the build starts"
        },
        "error": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "step": {
                  "type": "string",
                  "description": "Step the build failed at"
                },
                "message": {
                  "type": "string"
                },
                "code": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "step",
                "message",
                "code"
              ],
              "additionalProperties": false
            },
            {
              "type": "null"
            }
          ]
        },
        "createdAt": {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        "startedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 timestamp (UTC)"
            },
            {
              "type": "null"
            }
          ]
        },
        "finishedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 timestamp (UTC)"
            },
            {
              "type": "null"
            }
          ]
        },
        "pollAfterSec": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "description": "Seconds to wait before reading the build again; null once it has finished"
        }
      },
      "required": [
        "id",
        "appId",
        "status",
        "step",
        "imageBuildStatus",
        "source",
        "error",
        "createdAt",
        "startedAt",
        "finishedAt",
        "pollAfterSec"
      ],
      "additionalProperties": false,
      "description": "The build whose image is now deployed"
    },
    "redeploying": {
      "type": "boolean",
      "description": "The app is running and is being redeployed with the image"
    }
  },
  "required": [
    "build",
    "redeploying"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 app_source_unsupported`, `409 build_not_deployable`, `409 build_in_progress`.


# Custom domains

Reserve and verify DNS, attach custom domains and choose the default address.

Reserve and verify DNS, attach custom domains and choose the default address.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `manage_domain`

Custom domains for an app (its organization's members who can edit it). action=reserve returns the DNS record the user must create at their DNS provider; action=add verifies that record, issues the certificate and attaches the domain (domain\_not\_verified with the record until DNS is in place: retry after the user created it); action=remove detaches it; action=set\_default picks the domain the dashboard shows as the app's address (null for the Kinesis-issued one). get\_app lists attached domains under config.domains.

**Group:** `apps` · **Behavior:** Write

**Compact tool:** `apps_write` · **Action:** `manage_domain`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true`

### Permissions and REST equivalents

| Operation                 | REST                                                         | Required scopes |
| ------------------------- | ------------------------------------------------------------ | --------------- |
| `apps.domains.reserve`    | `POST /projects/{projectId}/apps/{appId}/domains/reserve`    | `apps:write`    |
| `apps.domains.add`        | `POST /projects/{projectId}/apps/{appId}/domains`            | `apps:write`    |
| `apps.domains.remove`     | `DELETE /projects/{projectId}/apps/{appId}/domains/{domain}` | `apps:write`    |
| `apps.domains.setDefault` | `POST /projects/{projectId}/apps/{appId}/domains/default`    | `apps:write`    |

### Arguments

| Argument    | Type                                           | Required | Description                                              |
| ----------- | ---------------------------------------------- | -------- | -------------------------------------------------------- |
| `action`    | `"reserve" / "add" / "remove" / "set_default"` | Yes      | What to do                                               |
| `projectId` | `string`                                       | Yes      | Project id                                               |
| `appId`     | `string`                                       | Yes      | App id                                                   |
| `domain`    | `string or null`                               | Yes      | Fully qualified domain name; null only with set\_default |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "reserve",
        "add",
        "remove",
        "set_default"
      ],
      "description": "What to do"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Project id"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "App id"
    },
    "domain": {
      "anyOf": [
        {
          "type": "string",
          "pattern": "^(?=.{4,253}$)(?!-)[a-z0-9-]{1,63}(?<!-)(\\.(?!-)[a-z0-9-]{1,63}(?<!-))+$",
          "description": "Fully qualified domain name, e.g. app.example.com"
        },
        {
          "type": "null"
        }
      ],
      "description": "Fully qualified domain name; null only with set_default"
    }
  },
  "required": [
    "action",
    "projectId",
    "appId",
    "domain"
  ],
  "additionalProperties": false
}
```

</details>

### Result

**`apps.domains.reserve`**

* `domain` (`string`): See the complete response schema below.
* `ready` (`boolean`): Verified with an unexpired certificate; add attaches it without further DNS steps
* `dnsRecord` (`object or null`): The record to create; null once no delegation is needed
* `certificate` (`object or null`): Validity of the issued certificate, when one exists

<details>

<summary>Response schema: apps.domains.reserve</summary>

```json
{
  "type": "object",
  "properties": {
    "domain": {
      "type": "string"
    },
    "ready": {
      "type": "boolean",
      "description": "Verified with an unexpired certificate; add attaches it without further DNS steps"
    },
    "dnsRecord": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "type": {
              "type": "string",
              "description": "Record type, e.g. CNAME or NS"
            },
            "name": {
              "type": "string",
              "description": "Record name to create"
            },
            "value": {
              "type": "string",
              "description": "Record value"
            }
          },
          "required": [
            "type",
            "name",
            "value"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "The record to create; null once no delegation is needed"
    },
    "certificate": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "notBefore": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time",
                  "description": "ISO 8601 timestamp (UTC)"
                },
                {
                  "type": "null"
                }
              ]
            },
            "notAfter": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time",
                  "description": "ISO 8601 timestamp (UTC)"
                },
                {
                  "type": "null"
                }
              ]
            }
          },
          "required": [
            "notBefore",
            "notAfter"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Validity of the issued certificate, when one exists"
    }
  },
  "required": [
    "domain",
    "ready",
    "dnsRecord",
    "certificate"
  ],
  "additionalProperties": false
}
```

</details>

\*\*\`apps.domains.add\`\*\*

* `domain` (`string`): See the complete response schema below.
* `domains` (`array<string>`): Custom domains attached to the app
* `defaultDomain` (`string or null`): Domain the dashboard shows as the app's address; null for the Kinesis-issued one

<details>

<summary>Response schema: apps.domains.add</summary>

```json
{
  "type": "object",
  "properties": {
    "domain": {
      "type": "string"
    },
    "domains": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Custom domains attached to the app"
    },
    "defaultDomain": {
      "description": "Domain the dashboard shows as the app's address; null for the Kinesis-issued one",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "domain",
    "domains",
    "defaultDomain"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 domain_not_verified`, `400 grid_to_serverless`, `400 no_suitable_machines`, `400 serverless_gpu_unavailable`, `429 grid_quota_exceeded`. **`apps.domains.remove`**

* `domain` (`string`): See the complete response schema below.
* `domains` (`array<string>`): Custom domains attached to the app
* `defaultDomain` (`string or null`): Domain the dashboard shows as the app's address; null for the Kinesis-issued one
* `deleted` (`true`): See the complete response schema below.

<details>

<summary>Response schema: apps.domains.remove</summary>

```json
{
  "type": "object",
  "properties": {
    "domain": {
      "type": "string"
    },
    "domains": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Custom domains attached to the app"
    },
    "defaultDomain": {
      "description": "Domain the dashboard shows as the app's address; null for the Kinesis-issued one",
      "type": [
        "string",
        "null"
      ]
    },
    "deleted": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "domain",
    "domains",
    "defaultDomain",
    "deleted"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 grid_to_serverless`, `400 no_suitable_machines`, `400 serverless_gpu_unavailable`, `429 grid_quota_exceeded`. **`apps.domains.setDefault`**

* `domains` (`array<string>`): Custom domains attached to the app
* `defaultDomain` (`string or null`): Domain the dashboard shows as the app's address; null for the Kinesis-issued one

<details>

<summary>Response schema: apps.domains.setDefault</summary>

```json
{
  "type": "object",
  "properties": {
    "domains": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Custom domains attached to the app"
    },
    "defaultDomain": {
      "description": "Domain the dashboard shows as the app's address; null for the Kinesis-issued one",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "domains",
    "defaultDomain"
  ],
  "additionalProperties": false
}
```

</details>


# GitHub

Browse connected GitHub accounts, repositories, branches, commits and Dockerfiles.

Browse connected GitHub accounts, repositories, branches, commits and Dockerfiles.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `browse_github`

Browse what the user connected through the Kinesis GitHub App, for create\_app's source. kind=accounts: the connected accounts with their installationId (and a connectUrl to connect one); repos: what an installation can read; branches; commits: the newest, or one by sha; dockerfiles: the Dockerfiles and the ports the chosen one exposes; contents: a directory listing (the root when path is omitted) or a text file. No Dockerfile: read the files that say how the repository is built, write one and pass it to create\_app as source.dockerfileContent.

**Group:** `github` · **Behavior:** Read

**Compact tool:** `browse_github` (direct call)

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                | REST                          | Required scopes |
| ------------------------ | ----------------------------- | --------------- |
| `vcs.github.accounts`    | `GET /vcs/github/accounts`    | `vcs:read`      |
| `vcs.github.repos`       | `GET /vcs/github/repos`       | `vcs:read`      |
| `vcs.github.branches`    | `GET /vcs/github/branches`    | `vcs:read`      |
| `vcs.github.commits`     | `GET /vcs/github/commits`     | `vcs:read`      |
| `vcs.github.dockerfiles` | `GET /vcs/github/dockerfiles` | `vcs:read`      |
| `vcs.github.contents`    | `GET /vcs/github/contents`    | `vcs:read`      |

### Arguments

| Argument         | Type                                                                         | Required | Description                                                           |
| ---------------- | ---------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------- |
| `kind`           | `"accounts" / "repos" / "branches" / "commits" / "dockerfiles" / "contents"` | Yes      | What to list                                                          |
| `installationId` | `integer`                                                                    | No       | Installation id; required for every kind but accounts                 |
| `repo`           | `string`                                                                     | No       | owner/name; required for every kind but accounts and repos            |
| `search`         | `string`                                                                     | No       | Case-insensitive substring of owner/name                              |
| `cursor`         | `string`                                                                     | No       | nextCursor from the previous page; omit for the first page            |
| `branch`         | `string`                                                                     | No       | Branch to list; default: the repository's default branch              |
| `sha`            | `string`                                                                     | No       | Look up this one commit instead of listing a branch                   |
| `limit`          | `integer`                                                                    | No       | Commits to return, 1 to 100 (default 20)                              |
| `contextDir`     | `string`                                                                     | No       | Build context directory (default /)                                   |
| `dockerfilePath` | `string`                                                                     | No       | Dockerfile path relative to contextDir (default Dockerfile)           |
| `path`           | `string`                                                                     | No       | File or directory, relative to the repository root; omit for the root |
| `maxBytes`       | `integer`                                                                    | No       | Bytes of a file to return, at most 262144 (default 65536)             |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "accounts",
        "repos",
        "branches",
        "commits",
        "dockerfiles",
        "contents"
      ],
      "description": "What to list"
    },
    "installationId": {
      "description": "Installation id; required for every kind but accounts",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    },
    "repo": {
      "description": "owner/name; required for every kind but accounts and repos",
      "type": "string",
      "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})\\/[A-Za-z0-9._-]{1,100}$"
    },
    "search": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "Case-insensitive substring of owner/name"
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "nextCursor from the previous page; omit for the first page"
    },
    "branch": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "description": "Branch to list; default: the repository's default branch"
    },
    "sha": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{7,40}$",
      "description": "Look up this one commit instead of listing a branch"
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "description": "Commits to return, 1 to 100 (default 20)"
    },
    "contextDir": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "Build context directory (default /)"
    },
    "dockerfilePath": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "Dockerfile path relative to contextDir (default Dockerfile)"
    },
    "path": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "File or directory, relative to the repository root; omit for the root"
    },
    "maxBytes": {
      "type": "integer",
      "minimum": 1,
      "maximum": 262144,
      "description": "Bytes of a file to return, at most 262144 (default 65536)"
    }
  },
  "required": [
    "kind"
  ],
  "additionalProperties": false
}
```

</details>

### Result

**`vcs.github.accounts`**

* `enabled` (`boolean`): GitHub deployments are enabled on this server
* `accounts` (`array<object>`): See the complete response schema below.
* `connectUrl` (`string`): Dashboard page where the user connects a GitHub account

<details>

<summary>Response schema: vcs.github.accounts</summary>

```json
{
  "type": "object",
  "properties": {
    "enabled": {
      "type": "boolean",
      "description": "GitHub deployments are enabled on this server"
    },
    "accounts": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "installationId": {
            "type": "integer",
            "description": "Installation id to pass to the other GitHub operations"
          },
          "login": {
            "type": "string",
            "description": "GitHub user or organization name"
          },
          "type": {
            "type": "string",
            "enum": [
              "user",
              "organization"
            ]
          },
          "avatarUrl": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "installationId",
          "login",
          "type",
          "avatarUrl"
        ],
        "additionalProperties": false
      }
    },
    "connectUrl": {
      "type": "string",
      "description": "Dashboard page where the user connects a GitHub account"
    }
  },
  "required": [
    "enabled",
    "accounts",
    "connectUrl"
  ],
  "additionalProperties": false
}
```

</details>

\*\*\`vcs.github.repos\`\*\*

* `repos` (`array<object>`): See the complete response schema below.
* `nextCursor` (`string or null`): Pass as cursor to fetch the next page; null on the last page

<details>

<summary>Response schema: vcs.github.repos</summary>

```json
{
  "type": "object",
  "properties": {
    "repos": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "GitHub's numeric repository id"
          },
          "name": {
            "type": "string",
            "description": "owner/name"
          },
          "defaultBranch": {
            "type": "string"
          },
          "private": {
            "type": "boolean"
          },
          "url": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "type": [
              "string",
              "null"
            ]
          },
          "updatedAt": {
            "type": "string",
            "description": "Last push or change, ISO 8601"
          }
        },
        "required": [
          "id",
          "name",
          "defaultBranch",
          "private",
          "url",
          "description",
          "language",
          "updatedAt"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": {
      "description": "Pass as cursor to fetch the next page; null on the last page",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "repos",
    "nextCursor"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `503 github_not_configured`, `404 github_installation_not_found`, `404 github_repo_not_found`, `502 upstream_github`. **`vcs.github.branches`**

* `defaultBranch` (`string`): See the complete response schema below.
* `branches` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: vcs.github.branches</summary>

```json
{
  "type": "object",
  "properties": {
    "defaultBranch": {
      "type": "string"
    },
    "branches": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "sha": {
            "type": "string"
          },
          "protected": {
            "type": "boolean"
          }
        },
        "required": [
          "name",
          "sha",
          "protected"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "defaultBranch",
    "branches"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `503 github_not_configured`, `404 github_installation_not_found`, `404 github_repo_not_found`, `502 upstream_github`. **`vcs.github.commits`**

* `branch` (`string or null`): Branch listed; null for a sha lookup
* `commits` (`array<object>`): Newest first

<details>

<summary>Response schema: vcs.github.commits</summary>

```json
{
  "type": "object",
  "properties": {
    "branch": {
      "description": "Branch listed; null for a sha lookup",
      "type": [
        "string",
        "null"
      ]
    },
    "commits": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "sha": {
            "type": "string"
          },
          "message": {
            "type": "string",
            "description": "First line of the commit message"
          },
          "author": {
            "type": "string",
            "description": "GitHub login of the author; empty when unknown"
          },
          "committedAt": {
            "type": "string",
            "description": "ISO 8601; empty when unknown"
          }
        },
        "required": [
          "sha",
          "message",
          "author",
          "committedAt"
        ],
        "additionalProperties": false
      },
      "description": "Newest first"
    }
  },
  "required": [
    "branch",
    "commits"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `503 github_not_configured`, `404 github_installation_not_found`, `404 github_repo_not_found`, `502 upstream_github`, `404 github_branch_not_found`, `404 github_commit_not_found`. **`vcs.github.dockerfiles`**

* `dockerfiles` (`array<string>`): Every Dockerfile path found, up to five directories deep; empty when there is none to build from
* `resolvedPathExists` (`boolean`): contextDir plus dockerfilePath names an existing file
* `truncated` (`boolean`): The repository tree was too large to list completely
* `exposedPorts` (`array<object>`): EXPOSE lines of the resolved Dockerfile
* `command` (`array<string> or null`): CMD of the resolved Dockerfile
* `entrypoint` (`array<string> or null`): ENTRYPOINT of the resolved Dockerfile

<details>

<summary>Response schema: vcs.github.dockerfiles</summary>

```json
{
  "type": "object",
  "properties": {
    "dockerfiles": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Every Dockerfile path found, up to five directories deep; empty when there is none to build from"
    },
    "resolvedPathExists": {
      "type": "boolean",
      "description": "contextDir plus dockerfilePath names an existing file"
    },
    "truncated": {
      "type": "boolean",
      "description": "The repository tree was too large to list completely"
    },
    "exposedPorts": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "port": {
            "type": "integer"
          },
          "protocol": {
            "type": "string",
            "enum": [
              "tcp",
              "udp"
            ]
          }
        },
        "required": [
          "port",
          "protocol"
        ],
        "additionalProperties": false
      },
      "description": "EXPOSE lines of the resolved Dockerfile"
    },
    "command": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "description": "CMD of the resolved Dockerfile"
    },
    "entrypoint": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "description": "ENTRYPOINT of the resolved Dockerfile"
    }
  },
  "required": [
    "dockerfiles",
    "resolvedPathExists",
    "truncated",
    "exposedPorts",
    "command",
    "entrypoint"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `503 github_not_configured`, `404 github_installation_not_found`, `404 github_repo_not_found`, `502 upstream_github`, `404 github_branch_not_found`. **`vcs.github.contents`**

* `branch` (`string`): See the complete response schema below.
* `path` (`string`): Path read; empty for the root
* `type` (`"dir" / "file" / "symlink" / "submodule"`): See the complete response schema below.
* `entries` (`array<object> or null`): A directory's entries, directories first, then by name; null for anything else
* `content` (`string or null`): A text file's content (UTF-8); null for a directory, a binary file or a file over 1 MB
* `size` (`integer or null`): A file's size in bytes; null otherwise
* `truncated` (`boolean`): A listing stopped at GitHub's 1,000 entries, or content stops at maxBytes, or the file was over 1 MB
* `binary` (`boolean`): The file is not text, so content is null
* `target` (`string or null`): A symlink's target; null otherwise

<details>

<summary>Response schema: vcs.github.contents</summary>

```json
{
  "type": "object",
  "properties": {
    "branch": {
      "type": "string"
    },
    "path": {
      "type": "string",
      "description": "Path read; empty for the root"
    },
    "type": {
      "type": "string",
      "enum": [
        "dir",
        "file",
        "symlink",
        "submodule"
      ]
    },
    "entries": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "path": {
                "type": "string",
                "description": "Relative to the repository root"
              },
              "type": {
                "type": "string",
                "enum": [
                  "file",
                  "dir",
                  "symlink",
                  "submodule"
                ]
              },
              "size": {
                "type": "integer",
                "description": "Bytes; 0 for a directory"
              }
            },
            "required": [
              "name",
              "path",
              "type",
              "size"
            ],
            "additionalProperties": false
          }
        },
        {
          "type": "null"
        }
      ],
      "description": "A directory's entries, directories first, then by name; null for anything else"
    },
    "content": {
      "description": "A text file's content (UTF-8); null for a directory, a binary file or a file over 1 MB",
      "type": [
        "string",
        "null"
      ]
    },
    "size": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "description": "A file's size in bytes; null otherwise"
    },
    "truncated": {
      "type": "boolean",
      "description": "A listing stopped at GitHub's 1,000 entries, or content stops at maxBytes, or the file was over 1 MB"
    },
    "binary": {
      "type": "boolean",
      "description": "The file is not text, so content is null"
    },
    "target": {
      "description": "A symlink's target; null otherwise",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "branch",
    "path",
    "type",
    "entries",
    "content",
    "size",
    "truncated",
    "binary",
    "target"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `503 github_not_configured`, `404 github_installation_not_found`, `404 github_repo_not_found`, `502 upstream_github`, `404 github_path_not_found`, `422 github_file_too_large`, `429 github_rate_limited`.


# Sharing and invitations

Manage access and invitations for projects, apps and grids.

Manage access and invitations for projects, apps and grids.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

Add existing organization members with `add_grantees`; invite new members with `invite_member`. Resource-grant values remain `viewer` and `editor`: `editor` means Developer in the portal and is capped by the member’s organization role. Grids accept `viewer` for use only; developers can deploy and viewers only inspect. The older invitation actions below handle existing invitations only. They do not create new resource invitations.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `add_grantees`

Add members of the resource's organization to a project, app or grid. No invitation: access starts at once. Each role is at most theirs in the organization (a viewer is added as viewer; grids take viewer only), and someone already added gets the new role. People outside the organization can't be added

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `add_grantees`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation              | REST                     | Required scopes      |
| ---------------------- | ------------------------ | -------------------- |
| `sharing.grantees.add` | `POST /sharing/grantees` | See conditions below |
| Conditional            | resourceType is project  | `projects:admin`     |
| Conditional            | resourceType is app      | `apps:admin`         |
| Conditional            | resourceType is grid     | `pools:admin`        |

### Arguments

| Argument       | Type                         | Required | Description                      |
| -------------- | ---------------------------- | -------- | -------------------------------- |
| `resourceType` | `"project" / "app" / "grid"` | Yes      | What is shared                   |
| `resourceId`   | `string`                     | Yes      | Project or app id, or grid id    |
| `people`       | `array<object>`              | Yes      | Members to add, each with a role |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "resourceType": {
      "type": "string",
      "enum": [
        "project",
        "app",
        "grid"
      ],
      "description": "What is shared"
    },
    "resourceId": {
      "type": "string",
      "minLength": 1,
      "description": "Project or app id, or grid id"
    },
    "people": {
      "minItems": 1,
      "maxItems": 50,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{24}$",
            "description": "A member's user id"
          },
          "role": {
            "type": "string",
            "enum": [
              "viewer",
              "editor"
            ],
            "description": "Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"
          }
        },
        "required": [
          "userId",
          "role"
        ],
        "additionalProperties": {}
      },
      "description": "Members to add, each with a role"
    }
  },
  "required": [
    "resourceType",
    "resourceId",
    "people"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `resourceType` (`"project" / "app" / "grid"`): What is shared
* `resourceId` (`string`): See the complete response schema below.
* `resourceName` (`string`): See the complete response schema below.
* `callerRole` (`"viewer" / "editor" / "owner"`): Legacy permission values: owner means an organization admin; editor means the member who created the resource
* `grantees` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: sharing.grantees.add</summary>

```json
{
  "type": "object",
  "properties": {
    "resourceType": {
      "type": "string",
      "enum": [
        "project",
        "app",
        "grid"
      ],
      "description": "What is shared"
    },
    "resourceId": {
      "type": "string"
    },
    "resourceName": {
      "type": "string"
    },
    "callerRole": {
      "type": "string",
      "enum": [
        "viewer",
        "editor",
        "owner"
      ],
      "description": "Legacy permission values: owner means an organization admin; editor means the member who created the resource"
    },
    "grantees": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "User id; what update and revoke take"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "viewer",
              "editor"
            ],
            "description": "Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"
          },
          "orgRole": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "admin",
                  "developer",
                  "viewer"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Their role in the resource's organization; null for someone outside it"
          },
          "grantedAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          }
        },
        "required": [
          "userId",
          "email",
          "name",
          "role",
          "orgRole",
          "grantedAt"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "resourceType",
    "resourceId",
    "resourceName",
    "callerRole",
    "grantees"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 not_member`.

## `list_grantees`

Who a project, app or grid is shared with, and as what

**Group:** `sharing` · **Behavior:** Read

**Compact tool:** `sharing` · **Action:** `list_grantees`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation               | REST                    | Required scopes      |
| ----------------------- | ----------------------- | -------------------- |
| `sharing.grantees.list` | `GET /sharing/grantees` | See conditions below |
| Conditional             | resourceType is project | `projects:admin`     |
| Conditional             | resourceType is app     | `apps:admin`         |
| Conditional             | resourceType is grid    | `pools:admin`        |

### Arguments

| Argument       | Type                         | Required | Description                   |
| -------------- | ---------------------------- | -------- | ----------------------------- |
| `resourceType` | `"project" / "app" / "grid"` | Yes      | What is shared                |
| `resourceId`   | `string`                     | Yes      | Project or app id, or grid id |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "resourceType": {
      "type": "string",
      "enum": [
        "project",
        "app",
        "grid"
      ],
      "description": "What is shared"
    },
    "resourceId": {
      "type": "string",
      "minLength": 1,
      "description": "Project or app id, or grid id"
    }
  },
  "required": [
    "resourceType",
    "resourceId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `resourceType` (`"project" / "app" / "grid"`): What is shared
* `resourceId` (`string`): See the complete response schema below.
* `resourceName` (`string`): See the complete response schema below.
* `callerRole` (`"viewer" / "editor" / "owner"`): Legacy permission values: owner means an organization admin; editor means the member who created the resource
* `grantees` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: sharing.grantees.list</summary>

```json
{
  "type": "object",
  "properties": {
    "resourceType": {
      "type": "string",
      "enum": [
        "project",
        "app",
        "grid"
      ],
      "description": "What is shared"
    },
    "resourceId": {
      "type": "string"
    },
    "resourceName": {
      "type": "string"
    },
    "callerRole": {
      "type": "string",
      "enum": [
        "viewer",
        "editor",
        "owner"
      ],
      "description": "Legacy permission values: owner means an organization admin; editor means the member who created the resource"
    },
    "grantees": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "User id; what update and revoke take"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "viewer",
              "editor"
            ],
            "description": "Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"
          },
          "orgRole": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "admin",
                  "developer",
                  "viewer"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Their role in the resource's organization; null for someone outside it"
          },
          "grantedAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          }
        },
        "required": [
          "userId",
          "email",
          "name",
          "role",
          "orgRole",
          "grantedAt"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "resourceType",
    "resourceId",
    "resourceName",
    "callerRole",
    "grantees"
  ],
  "additionalProperties": false
}
```

</details>

## `set_grantee_role`

Change a grantee's role on a project or app (grids are shared as viewer only). On a grid, taking an old editor grant away also releases the machines and spot groups they added. Not on the caller's own grant.

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `set_grantee_role`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                 | REST                                      | Required scopes      |
| ------------------------- | ----------------------------------------- | -------------------- |
| `sharing.grantees.update` | `PATCH /sharing/grantees/{granteeUserId}` | See conditions below |
| Conditional               | resourceType is project                   | `projects:admin`     |
| Conditional               | resourceType is app                       | `apps:admin`         |
| Conditional               | resourceType is grid                      | `pools:admin`        |

### Arguments

| Argument        | Type                         | Required | Description                                                                                                                                           |
| --------------- | ---------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `resourceType`  | `"project" / "app" / "grid"` | Yes      | What is shared                                                                                                                                        |
| `resourceId`    | `string`                     | Yes      | Project or app id, or grid id                                                                                                                         |
| `role`          | `"viewer" / "editor"`        | Yes      | Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read |
| `granteeUserId` | `string`                     | Yes      | User id of the grantee, from the grantees list                                                                                                        |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "resourceType": {
      "type": "string",
      "enum": [
        "project",
        "app",
        "grid"
      ],
      "description": "What is shared"
    },
    "resourceId": {
      "type": "string",
      "minLength": 1,
      "description": "Project or app id, or grid id"
    },
    "role": {
      "type": "string",
      "enum": [
        "viewer",
        "editor"
      ],
      "description": "Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"
    },
    "granteeUserId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "User id of the grantee, from the grantees list"
    }
  },
  "required": [
    "resourceType",
    "resourceId",
    "role",
    "granteeUserId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `resourceType` (`"project" / "app" / "grid"`): What is shared
* `resourceId` (`string`): See the complete response schema below.
* `resourceName` (`string`): See the complete response schema below.
* `callerRole` (`"viewer" / "editor" / "owner"`): Legacy permission values: owner means an organization admin; editor means the member who created the resource
* `grantees` (`array<object>`): See the complete response schema below.
* `warning` (`string or null`): Set when a grid could not release the grantee's machines or spot groups; the access change stands

<details>

<summary>Response schema: sharing.grantees.update</summary>

```json
{
  "type": "object",
  "properties": {
    "resourceType": {
      "type": "string",
      "enum": [
        "project",
        "app",
        "grid"
      ],
      "description": "What is shared"
    },
    "resourceId": {
      "type": "string"
    },
    "resourceName": {
      "type": "string"
    },
    "callerRole": {
      "type": "string",
      "enum": [
        "viewer",
        "editor",
        "owner"
      ],
      "description": "Legacy permission values: owner means an organization admin; editor means the member who created the resource"
    },
    "grantees": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "description": "User id; what update and revoke take"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "viewer",
              "editor"
            ],
            "description": "Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"
          },
          "orgRole": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "admin",
                  "developer",
                  "viewer"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Their role in the resource's organization; null for someone outside it"
          },
          "grantedAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          }
        },
        "required": [
          "userId",
          "email",
          "name",
          "role",
          "orgRole",
          "grantedAt"
        ],
        "additionalProperties": false
      }
    },
    "warning": {
      "description": "Set when a grid could not release the grantee's machines or spot groups; the access change stands",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "resourceType",
    "resourceId",
    "resourceName",
    "callerRole",
    "grantees",
    "warning"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `403 forbidden`.

## `revoke_access`

Remove a grantee's access. On a grid, machines and spot groups a former co-manager added leave it too. Not on the caller's own grant (use leave).

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `revoke_access`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                 | REST                                       | Required scopes      |
| ------------------------- | ------------------------------------------ | -------------------- |
| `sharing.grantees.revoke` | `DELETE /sharing/grantees/{granteeUserId}` | See conditions below |
| Conditional               | resourceType is project                    | `projects:admin`     |
| Conditional               | resourceType is app                        | `apps:admin`         |
| Conditional               | resourceType is grid                       | `pools:admin`        |

### Arguments

| Argument        | Type                         | Required | Description                                    |
| --------------- | ---------------------------- | -------- | ---------------------------------------------- |
| `granteeUserId` | `string`                     | Yes      | User id of the grantee, from the grantees list |
| `resourceType`  | `"project" / "app" / "grid"` | Yes      | What is shared                                 |
| `resourceId`    | `string`                     | Yes      | Project or app id, or grid id                  |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "granteeUserId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "User id of the grantee, from the grantees list"
    },
    "resourceType": {
      "type": "string",
      "enum": [
        "project",
        "app",
        "grid"
      ],
      "description": "What is shared"
    },
    "resourceId": {
      "type": "string",
      "minLength": 1,
      "description": "Project or app id, or grid id"
    }
  },
  "required": [
    "granteeUserId",
    "resourceType",
    "resourceId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `warning` (`string or null`): Set when a grid could not release the grantee's machines or spot groups; the access change stands

<details>

<summary>Response schema: sharing.grantees.revoke</summary>

```json
{
  "type": "object",
  "properties": {
    "warning": {
      "description": "Set when a grid could not release the grantee's machines or spot groups; the access change stands",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "warning"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `403 forbidden`.

## `leave_shared_resource`

Give up the caller's own access to a shared project, app or grid. On a grid, their machines and spot groups leave with them.

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `leave_shared_resource`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation       | REST                  | Required scopes |
| --------------- | --------------------- | --------------- |
| `sharing.leave` | `POST /sharing/leave` | `account:write` |

### Arguments

| Argument       | Type                         | Required | Description                   |
| -------------- | ---------------------------- | -------- | ----------------------------- |
| `resourceType` | `"project" / "app" / "grid"` | Yes      | What is shared                |
| `resourceId`   | `string`                     | Yes      | Project or app id, or grid id |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "resourceType": {
      "type": "string",
      "enum": [
        "project",
        "app",
        "grid"
      ],
      "description": "What is shared"
    },
    "resourceId": {
      "type": "string",
      "minLength": 1,
      "description": "Project or app id, or grid id"
    }
  },
  "required": [
    "resourceType",
    "resourceId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `warning` (`string or null`): Set when a grid could not release the grantee's machines or spot groups; the access change stands

<details>

<summary>Response schema: sharing.leave</summary>

```json
{
  "type": "object",
  "properties": {
    "warning": {
      "description": "Set when a grid could not release the grantee's machines or spot groups; the access change stands",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "warning"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 invalid_request`.

## `list_invitations`

Invitations on a resource the caller manages, or those the caller sent. Pending and accepted ones: by resourceType and resourceId, or the sent ones within the credential's admin scopes and project restrictions

**Group:** `sharing` · **Behavior:** Read

**Compact tool:** `sharing` · **Action:** `list_invitations`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                  | REST                       | Required scopes      |
| -------------------------- | -------------------------- | -------------------- |
| `sharing.invitations.list` | `GET /sharing/invitations` | See conditions below |
| Conditional                | resourceType is project    | `projects:admin`     |
| Conditional                | resourceType is app        | `apps:admin`         |
| Conditional                | resourceType is grid       | `pools:admin`        |

### Arguments

| Argument       | Type                         | Required | Description                                               |
| -------------- | ---------------------------- | -------- | --------------------------------------------------------- |
| `resourceType` | `"project" / "app" / "grid"` | No       | What is shared                                            |
| `resourceId`   | `string`                     | No       | With resourceType: the resource whose invitations to list |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "resourceType": {
      "type": "string",
      "enum": [
        "project",
        "app",
        "grid"
      ],
      "description": "What is shared"
    },
    "resourceId": {
      "type": "string",
      "minLength": 1,
      "description": "With resourceType: the resource whose invitations to list"
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `invitations` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: sharing.invitations.list</summary>

```json
{
  "type": "object",
  "properties": {
    "invitations": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Invitation id"
          },
          "resourceType": {
            "type": "string",
            "enum": [
              "project",
              "app",
              "grid"
            ],
            "description": "What is shared"
          },
          "resourceId": {
            "type": "string"
          },
          "resourceName": {
            "type": "string"
          },
          "inviteeEmail": {
            "type": "string"
          },
          "invitedByEmail": {
            "type": "string",
            "description": "Who sent it"
          },
          "role": {
            "type": "string",
            "enum": [
              "viewer",
              "editor"
            ],
            "description": "Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "accepted",
              "rejected"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          },
          "acceptedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "resourceType",
          "resourceId",
          "resourceName",
          "inviteeEmail",
          "invitedByEmail",
          "role",
          "status",
          "createdAt",
          "acceptedAt"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "invitations"
  ],
  "additionalProperties": false
}
```

</details>

## `cancel_invitation`

Cancel an invitation the caller manages. Cancelling an accepted invitation revokes the access it granted. Needs the admin scope of the invitation's resource type.

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `cancel_invitation`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                    | REST                                         | Required scopes                                                                     |
| ---------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------- |
| `sharing.invitations.cancel` | `DELETE /sharing/invitations/{invitationId}` | Admin scope of the invitation’s resource: projects:admin, apps:admin or pools:admin |

### Arguments

| Argument       | Type     | Required | Description   |
| -------------- | -------- | -------- | ------------- |
| `invitationId` | `string` | Yes      | Invitation id |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "invitationId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Invitation id"
    }
  },
  "required": [
    "invitationId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `warning` (`string or null`): Set when a grid could not release the grantee's machines or spot groups; the access change stands

<details>

<summary>Response schema: sharing.invitations.cancel</summary>

```json
{
  "type": "object",
  "properties": {
    "warning": {
      "description": "Set when a grid could not release the grantee's machines or spot groups; the access change stands",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "warning"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `403 forbidden`.

## `list_received_invitations`

Pending invitations addressed to the caller

**Group:** `sharing` · **Behavior:** Read

**Compact tool:** `sharing` · **Action:** `list_received_invitations`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                      | REST                                | Required scopes |
| ------------------------------ | ----------------------------------- | --------------- |
| `sharing.invitations.received` | `GET /sharing/invitations/received` | `account:read`  |

### Arguments

No arguments. Send an empty object.

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

</details>

### Result

* `invitations` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: sharing.invitations.received</summary>

```json
{
  "type": "object",
  "properties": {
    "invitations": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Invitation id"
          },
          "resourceType": {
            "type": "string",
            "enum": [
              "project",
              "app",
              "grid"
            ],
            "description": "What is shared"
          },
          "resourceId": {
            "type": "string"
          },
          "resourceName": {
            "type": "string"
          },
          "inviteeEmail": {
            "type": "string"
          },
          "invitedByEmail": {
            "type": "string",
            "description": "Who sent it"
          },
          "role": {
            "type": "string",
            "enum": [
              "viewer",
              "editor"
            ],
            "description": "Resource grant values: viewer reads; editor means Developer. Grids take viewer for use only; organization developers may deploy and viewers only read"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "accepted",
              "rejected"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          },
          "acceptedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "resourceType",
          "resourceId",
          "resourceName",
          "inviteeEmail",
          "invitedByEmail",
          "role",
          "status",
          "createdAt",
          "acceptedAt"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "invitations"
  ],
  "additionalProperties": false
}
```

</details>

## `accept_invitation`

Accept an existing resource invitation addressed to the caller. For new team access, use organization invitations and add members to the resource

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `accept_invitation`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                    | REST                                              | Required scopes |
| ---------------------------- | ------------------------------------------------- | --------------- |
| `sharing.invitations.accept` | `POST /sharing/invitations/{invitationId}/accept` | `account:write` |

### Arguments

| Argument       | Type     | Required | Description   |
| -------------- | -------- | -------- | ------------- |
| `invitationId` | `string` | Yes      | Invitation id |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "invitationId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Invitation id"
    }
  },
  "required": [
    "invitationId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `resourceType` (`"project" / "app" / "grid"`): What is shared
* `resourceId` (`string`): See the complete response schema below.
* `resourceName` (`string`): See the complete response schema below.

<details>

<summary>Response schema: sharing.invitations.accept</summary>

```json
{
  "type": "object",
  "properties": {
    "resourceType": {
      "type": "string",
      "enum": [
        "project",
        "app",
        "grid"
      ],
      "description": "What is shared"
    },
    "resourceId": {
      "type": "string"
    },
    "resourceName": {
      "type": "string"
    }
  },
  "required": [
    "resourceType",
    "resourceId",
    "resourceName"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 no_org`.

## `decline_invitation`

Decline an invitation addressed to the caller

**Group:** `sharing` · **Behavior:** Write

**Compact tool:** `sharing_write` · **Action:** `decline_invitation`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                     | REST                                               | Required scopes |
| ----------------------------- | -------------------------------------------------- | --------------- |
| `sharing.invitations.decline` | `POST /sharing/invitations/{invitationId}/decline` | `account:write` |

### Arguments

| Argument       | Type     | Required | Description   |
| -------------- | -------- | -------- | ------------- |
| `invitationId` | `string` | Yes      | Invitation id |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "invitationId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Invitation id"
    }
  },
  "required": [
    "invitationId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `declined` (`true`): See the complete response schema below.

<details>

<summary>Response schema: sharing.invitations.decline</summary>

```json
{
  "type": "object",
  "properties": {
    "declined": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "declined"
  ],
  "additionalProperties": false
}
```

</details>


# Grids

Compose and manage the grids that apps target for dedicated capacity.

Compose and manage the grids that apps target for dedicated capacity.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

Only organization admins create and manage grids. Developers can deploy to accessible grids; viewers can inspect them. Grid quota policies are configured in the portal, not through these public actions. Quotas count apps, running containers and GPUs against the app’s creator.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `list_grids`

List the grids the caller can see: the organization's, then those shared with it

**Group:** `grids` · **Behavior:** Read

**Compact tool:** `grids` · **Action:** `list_grids`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation    | REST         | Required scopes |
| ------------ | ------------ | --------------- |
| `grids.list` | `GET /grids` | `pools:read`    |

### Arguments

| Argument | Type      | Required | Description                                                                                      |
| -------- | --------- | -------- | ------------------------------------------------------------------------------------------------ |
| `limit`  | `integer` | No       | Items per page, 1 to 200 (default 50)                                                            |
| `cursor` | `string`  | No       | nextCursor from the previous page; omit for the first page                                       |
| `org`    | `string`  | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 200,
      "description": "Items per page, 1 to 200 (default 50)"
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "nextCursor from the previous page; omit for the first page"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `grids` (`array<object>`): See the complete response schema below.
* `nextCursor` (`string or null`): Pass as cursor to fetch the next page; null on the last page

<details>

<summary>Response schema: grids.list</summary>

```json
{
  "type": "object",
  "properties": {
    "grids": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Grid id"
          },
          "name": {
            "type": "string"
          },
          "org": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "slug",
              "name"
            ],
            "additionalProperties": false,
            "description": "The organization that owns it"
          },
          "access": {
            "type": "object",
            "properties": {
              "role": {
                "type": "string",
                "enum": [
                  "admin",
                  "developer",
                  "viewer"
                ],
                "description": "The caller's role on it"
              },
              "outside": {
                "type": "boolean",
                "description": "Shared with the caller from another organization"
              },
              "isCreator": {
                "type": "boolean",
                "description": "The caller created it"
              }
            },
            "required": [
              "role",
              "outside",
              "isCreator"
            ],
            "additionalProperties": false
          },
          "visibility": {
            "type": "string",
            "enum": [
              "private",
              "org"
            ],
            "description": "private: admins, its creator and the people added; org: everyone in the organization"
          },
          "createdBy": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string",
                    "description": "User id, as members.list and the sharing calls use it"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "email": {
                    "type": "string"
                  }
                },
                "required": [
                  "userId",
                  "name",
                  "email"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
          },
          "nodes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Addresses of the servers in the grid"
          },
          "autoCreated": {
            "type": "boolean",
            "description": "Created by the platform rather than the user"
          },
          "priorityMode": {
            "type": "string",
            "enum": [
              "absolute",
              "weighted"
            ],
            "description": "How app priorities apply when the grid is contended"
          },
          "needsReconciliation": {
            "type": "boolean",
            "description": "Membership changes are still being applied"
          },
          "volumes": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "mountPath": {
                  "type": "string"
                },
                "maxSize": {
                  "type": "number"
                },
                "tag": {
                  "type": "string"
                }
              },
              "required": [
                "mountPath",
                "maxSize",
                "tag"
              ],
              "additionalProperties": false
            }
          },
          "networks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "cidr": {
                  "type": "string"
                },
                "tag": {
                  "type": "string"
                }
              },
              "required": [
                "cidr",
                "tag"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "id",
          "name",
          "org",
          "access",
          "visibility",
          "createdBy",
          "nodes",
          "autoCreated",
          "priorityMode",
          "needsReconciliation",
          "volumes",
          "networks"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": {
      "description": "Pass as cursor to fetch the next page; null on the last page",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "grids",
    "nextCursor"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `create_grid`

Create a grid in the organization (admins), optionally with servers attached

**Group:** `grids` · **Behavior:** Write

**Compact tool:** `grids_write` · **Action:** `create_grid`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation      | REST          | Required scopes |
| -------------- | ------------- | --------------- |
| `grids.create` | `POST /grids` | `pools:write`   |

### Arguments

| Argument         | Type                | Required | Description                                                                                                                                                           |
| ---------------- | ------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`           | `string`            | Yes      | Grid name                                                                                                                                                             |
| `visibility`     | `"private" / "org"` | No       | Who in the organization can see and deploy to it; private by default                                                                                                  |
| `volumes`        | `array<object>`     | No       | Shared volumes available to apps on the grid; replaces the stored list                                                                                                |
| `networks`       | `array<object>`     | No       | Networks available to apps on the grid; replaces the stored list                                                                                                      |
| `nodes`          | `array<string>`     | No       | Addresses of servers to attach                                                                                                                                        |
| `idempotencyKey` | `string`            | No       | Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice |
| `org`            | `string`            | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one                                                                      |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "Grid name"
    },
    "visibility": {
      "type": "string",
      "enum": [
        "private",
        "org"
      ],
      "description": "Who in the organization can see and deploy to it; private by default"
    },
    "volumes": {
      "maxItems": 16,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "mountPath": {
            "type": "string",
            "minLength": 1,
            "description": "Host path mounted into apps on the grid"
          },
          "maxSize": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Size cap in bytes; 0 for unlimited"
          },
          "tag": {
            "type": "string",
            "description": "Label apps use to pick the volume"
          }
        },
        "required": [
          "mountPath",
          "maxSize"
        ],
        "additionalProperties": false
      },
      "description": "Shared volumes available to apps on the grid; replaces the stored list"
    },
    "networks": {
      "maxItems": 16,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "cidr": {
            "type": "string",
            "minLength": 1,
            "description": "Network range in CIDR notation"
          },
          "tag": {
            "type": "string",
            "description": "Label apps use to pick the network"
          }
        },
        "required": [
          "cidr"
        ],
        "additionalProperties": false
      },
      "description": "Networks available to apps on the grid; replaces the stored list"
    },
    "nodes": {
      "maxItems": 100,
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^0x[0-9a-fA-F]{40}$"
      },
      "description": "Addresses of servers to attach"
    },
    "idempotencyKey": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "pattern": "^\\S+$",
      "description": "Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "name"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Grid id
* `name` (`string`): See the complete response schema below.
* `org` (`object`): The organization that owns it
* `access` (`object`): See the complete response schema below.
* `visibility` (`"private" / "org"`): private: admins, its creator and the people added; org: everyone in the organization
* `createdBy` (`object or null`): Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read
* `nodes` (`array<string>`): Addresses of the servers in the grid
* `autoCreated` (`boolean`): Created by the platform rather than the user
* `priorityMode` (`"absolute" / "weighted"`): How app priorities apply when the grid is contended
* `needsReconciliation` (`boolean`): Membership changes are still being applied
* `volumes` (`array<object>`): See the complete response schema below.
* `networks` (`array<object>`): See the complete response schema below.
* `warning` (`string or null`): Set when the grid was created but a follow-up step failed

<details>

<summary>Response schema: grids.create</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Grid id"
    },
    "name": {
      "type": "string"
    },
    "org": {
      "type": "object",
      "properties": {
        "slug": {
          "type": "string"
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "slug",
        "name"
      ],
      "additionalProperties": false,
      "description": "The organization that owns it"
    },
    "access": {
      "type": "object",
      "properties": {
        "role": {
          "type": "string",
          "enum": [
            "admin",
            "developer",
            "viewer"
          ],
          "description": "The caller's role on it"
        },
        "outside": {
          "type": "boolean",
          "description": "Shared with the caller from another organization"
        },
        "isCreator": {
          "type": "boolean",
          "description": "The caller created it"
        }
      },
      "required": [
        "role",
        "outside",
        "isCreator"
      ],
      "additionalProperties": false
    },
    "visibility": {
      "type": "string",
      "enum": [
        "private",
        "org"
      ],
      "description": "private: admins, its creator and the people added; org: everyone in the organization"
    },
    "createdBy": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "userId": {
              "type": "string",
              "description": "User id, as members.list and the sharing calls use it"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "email": {
              "type": "string"
            }
          },
          "required": [
            "userId",
            "name",
            "email"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
    },
    "nodes": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Addresses of the servers in the grid"
    },
    "autoCreated": {
      "type": "boolean",
      "description": "Created by the platform rather than the user"
    },
    "priorityMode": {
      "type": "string",
      "enum": [
        "absolute",
        "weighted"
      ],
      "description": "How app priorities apply when the grid is contended"
    },
    "needsReconciliation": {
      "type": "boolean",
      "description": "Membership changes are still being applied"
    },
    "volumes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "mountPath": {
            "type": "string"
          },
          "maxSize": {
            "type": "number"
          },
          "tag": {
            "type": "string"
          }
        },
        "required": [
          "mountPath",
          "maxSize",
          "tag"
        ],
        "additionalProperties": false
      }
    },
    "networks": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "cidr": {
            "type": "string"
          },
          "tag": {
            "type": "string"
          }
        },
        "required": [
          "cidr",
          "tag"
        ],
        "additionalProperties": false
      }
    },
    "warning": {
      "description": "Set when the grid was created but a follow-up step failed",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "id",
    "name",
    "org",
    "access",
    "visibility",
    "createdBy",
    "nodes",
    "autoCreated",
    "priorityMode",
    "needsReconciliation",
    "volumes",
    "networks",
    "warning"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `update_grid`

Change a grid's settings, visibility, membership or priority mode (admins). Membership changes are explicit lists of servers to add and to remove.

**Group:** `grids` · **Behavior:** Write

**Compact tool:** `grids_write` · **Action:** `update_grid`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation      | REST                    | Required scopes |
| -------------- | ----------------------- | --------------- |
| `grids.update` | `PATCH /grids/{gridId}` | `pools:write`   |

### Arguments

| Argument       | Type                      | Required | Description                                                                          |
| -------------- | ------------------------- | -------- | ------------------------------------------------------------------------------------ |
| `name`         | `string`                  | No       | Grid name                                                                            |
| `visibility`   | `"private" / "org"`       | No       | private: admins, its creator and the people added; org: everyone in the organization |
| `volumes`      | `array<object>`           | No       | Shared volumes available to apps on the grid; replaces the stored list               |
| `networks`     | `array<object>`           | No       | Networks available to apps on the grid; replaces the stored list                     |
| `addNodes`     | `array<string>`           | No       | Servers to add to the grid                                                           |
| `removeNodes`  | `array<string>`           | No       | Servers to remove from the grid                                                      |
| `priorityMode` | `"absolute" / "weighted"` | No       | How app priorities apply when the grid is contended                                  |
| `gridId`       | `string`                  | Yes      | Grid id                                                                              |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100,
      "description": "Grid name"
    },
    "visibility": {
      "type": "string",
      "enum": [
        "private",
        "org"
      ],
      "description": "private: admins, its creator and the people added; org: everyone in the organization"
    },
    "volumes": {
      "maxItems": 16,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "mountPath": {
            "type": "string",
            "minLength": 1,
            "description": "Host path mounted into apps on the grid"
          },
          "maxSize": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Size cap in bytes; 0 for unlimited"
          },
          "tag": {
            "type": "string",
            "description": "Label apps use to pick the volume"
          }
        },
        "required": [
          "mountPath",
          "maxSize"
        ],
        "additionalProperties": false
      },
      "description": "Shared volumes available to apps on the grid; replaces the stored list"
    },
    "networks": {
      "maxItems": 16,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "cidr": {
            "type": "string",
            "minLength": 1,
            "description": "Network range in CIDR notation"
          },
          "tag": {
            "type": "string",
            "description": "Label apps use to pick the network"
          }
        },
        "required": [
          "cidr"
        ],
        "additionalProperties": false
      },
      "description": "Networks available to apps on the grid; replaces the stored list"
    },
    "addNodes": {
      "maxItems": 500,
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^0x[0-9a-fA-F]{40}$"
      },
      "description": "Servers to add to the grid"
    },
    "removeNodes": {
      "maxItems": 500,
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^0x[0-9a-fA-F]{40}$"
      },
      "description": "Servers to remove from the grid"
    },
    "priorityMode": {
      "type": "string",
      "enum": [
        "absolute",
        "weighted"
      ],
      "description": "How app priorities apply when the grid is contended"
    },
    "gridId": {
      "type": "string",
      "minLength": 1,
      "description": "Grid id"
    }
  },
  "required": [
    "gridId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Grid id
* `name` (`string`): See the complete response schema below.
* `org` (`object`): The organization that owns it
* `access` (`object`): See the complete response schema below.
* `visibility` (`"private" / "org"`): private: admins, its creator and the people added; org: everyone in the organization
* `createdBy` (`object or null`): Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read
* `nodes` (`array<string>`): Addresses of the servers in the grid
* `autoCreated` (`boolean`): Created by the platform rather than the user
* `priorityMode` (`"absolute" / "weighted"`): How app priorities apply when the grid is contended
* `needsReconciliation` (`boolean`): Membership changes are still being applied
* `volumes` (`array<object>`): See the complete response schema below.
* `networks` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: grids.update</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Grid id"
    },
    "name": {
      "type": "string"
    },
    "org": {
      "type": "object",
      "properties": {
        "slug": {
          "type": "string"
        },
        "name": {
          "type": "string"
        }
      },
      "required": [
        "slug",
        "name"
      ],
      "additionalProperties": false,
      "description": "The organization that owns it"
    },
    "access": {
      "type": "object",
      "properties": {
        "role": {
          "type": "string",
          "enum": [
            "admin",
            "developer",
            "viewer"
          ],
          "description": "The caller's role on it"
        },
        "outside": {
          "type": "boolean",
          "description": "Shared with the caller from another organization"
        },
        "isCreator": {
          "type": "boolean",
          "description": "The caller created it"
        }
      },
      "required": [
        "role",
        "outside",
        "isCreator"
      ],
      "additionalProperties": false
    },
    "visibility": {
      "type": "string",
      "enum": [
        "private",
        "org"
      ],
      "description": "private: admins, its creator and the people added; org: everyone in the organization"
    },
    "createdBy": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "userId": {
              "type": "string",
              "description": "User id, as members.list and the sharing calls use it"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "email": {
              "type": "string"
            }
          },
          "required": [
            "userId",
            "name",
            "email"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Who created it. Null when nobody is on record, for another organization's, and for a credential without members:read"
    },
    "nodes": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Addresses of the servers in the grid"
    },
    "autoCreated": {
      "type": "boolean",
      "description": "Created by the platform rather than the user"
    },
    "priorityMode": {
      "type": "string",
      "enum": [
        "absolute",
        "weighted"
      ],
      "description": "How app priorities apply when the grid is contended"
    },
    "needsReconciliation": {
      "type": "boolean",
      "description": "Membership changes are still being applied"
    },
    "volumes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "mountPath": {
            "type": "string"
          },
          "maxSize": {
            "type": "number"
          },
          "tag": {
            "type": "string"
          }
        },
        "required": [
          "mountPath",
          "maxSize",
          "tag"
        ],
        "additionalProperties": false
      }
    },
    "networks": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "cidr": {
            "type": "string"
          },
          "tag": {
            "type": "string"
          }
        },
        "required": [
          "cidr",
          "tag"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "id",
    "name",
    "org",
    "access",
    "visibility",
    "createdBy",
    "nodes",
    "autoCreated",
    "priorityMode",
    "needsReconciliation",
    "volumes",
    "networks"
  ],
  "additionalProperties": false
}
```

</details>

## `set_server_priority`

Set a grid member's scheduling priority, -100 to 100. Higher wins when the grid is contended; -100 means backup only. Admins of the grid's organization.

**Group:** `grids` · **Behavior:** Write

**Compact tool:** `grids_write` · **Action:** `set_server_priority`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                   | REST                                      | Required scopes |
| --------------------------- | ----------------------------------------- | --------------- |
| `grids.servers.setPriority` | `PATCH /grids/{gridId}/servers/{address}` | `pools:write`   |

### Arguments

| Argument   | Type      | Required | Description                                  |
| ---------- | --------- | -------- | -------------------------------------------- |
| `priority` | `integer` | Yes      | -100 (backup only) to 100                    |
| `gridId`   | `string`  | Yes      | Grid id                                      |
| `address`  | `string`  | Yes      | Server address; must be a member of the grid |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "priority": {
      "type": "integer",
      "minimum": -100,
      "maximum": 100,
      "description": "-100 (backup only) to 100"
    },
    "gridId": {
      "type": "string",
      "minLength": 1,
      "description": "Grid id"
    },
    "address": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$",
      "description": "Server address; must be a member of the grid"
    }
  },
  "required": [
    "priority",
    "gridId",
    "address"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `gridId` (`string`): See the complete response schema below.
* `address` (`string`): See the complete response schema below.
* `priority` (`integer`): See the complete response schema below.

<details>

<summary>Response schema: grids.servers.setPriority</summary>

```json
{
  "type": "object",
  "properties": {
    "gridId": {
      "type": "string"
    },
    "address": {
      "type": "string"
    },
    "priority": {
      "type": "integer"
    }
  },
  "required": [
    "gridId",
    "address",
    "priority"
  ],
  "additionalProperties": false
}
```

</details>

## `control_grid_app`

Start or stop an app on a grid as an admin of the grid's organization. No role on the app is needed (action=start or stop); its creator is notified with the reason. Starting bills the app's organization.

**Group:** `grids` · **Behavior:** Write

**Compact tool:** `grids_write` · **Action:** `control_grid_app`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: true`

### Permissions and REST equivalents

| Operation          | REST                                        | Required scopes                |
| ------------------ | ------------------------------------------- | ------------------------------ |
| `grids.apps.start` | `POST /grids/{gridId}/apps/{appGuid}/start` | `pools:write`, `compute:spend` |
| `grids.apps.stop`  | `POST /grids/{gridId}/apps/{appGuid}/stop`  | `pools:write`                  |

### Arguments

| Argument  | Type               | Required | Description                                                     |
| --------- | ------------------ | -------- | --------------------------------------------------------------- |
| `action`  | `"start" / "stop"` | Yes      | What to do                                                      |
| `gridId`  | `string`           | Yes      | Grid id                                                         |
| `appGuid` | `string`           | Yes      | Runtime identifier of the app (guid on list\_apps, get\_server) |
| `reason`  | `string`           | No       | Shown to the app's creator in their notification                |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "start",
        "stop"
      ],
      "description": "What to do"
    },
    "gridId": {
      "type": "string",
      "minLength": 1,
      "description": "Grid id"
    },
    "appGuid": {
      "type": "string",
      "minLength": 1,
      "description": "Runtime identifier of the app (guid on list_apps, get_server)"
    },
    "reason": {
      "type": "string",
      "maxLength": 300,
      "description": "Shown to the app's creator in their notification"
    }
  },
  "required": [
    "action",
    "gridId",
    "appGuid"
  ],
  "additionalProperties": false
}
```

</details>

### Result

**`grids.apps.start`**

* `gridId` (`string`): See the complete response schema below.
* `appGuid` (`string`): See the complete response schema below.
* `appId` (`string or null`): The app's id when it is known to the portal
* `appName` (`string or null`): See the complete response schema below.

<details>

<summary>Response schema: grids.apps.start</summary>

```json
{
  "type": "object",
  "properties": {
    "gridId": {
      "type": "string"
    },
    "appGuid": {
      "type": "string"
    },
    "appId": {
      "description": "The app's id when it is known to the portal",
      "type": [
        "string",
        "null"
      ]
    },
    "appName": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "gridId",
    "appGuid",
    "appId",
    "appName"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `403 forbidden`, `409 app_not_on_grid`. **`grids.apps.stop`**

* `gridId` (`string`): See the complete response schema below.
* `appGuid` (`string`): See the complete response schema below.
* `appId` (`string or null`): The app's id when it is known to the portal
* `appName` (`string or null`): See the complete response schema below.

<details>

<summary>Response schema: grids.apps.stop</summary>

```json
{
  "type": "object",
  "properties": {
    "gridId": {
      "type": "string"
    },
    "appGuid": {
      "type": "string"
    },
    "appId": {
      "description": "The app's id when it is known to the portal",
      "type": [
        "string",
        "null"
      ]
    },
    "appName": {
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "gridId",
    "appGuid",
    "appId",
    "appName"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `403 forbidden`, `409 app_not_on_grid`.

## `delete_grid`

Delete a grid (admins). Its servers are kept; sharing grants and invitations are retired.

**Group:** `grids` · **Behavior:** Write

**Compact tool:** `grids_write` · **Action:** `delete_grid`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

Requires `confirm: true` for this irreversible action.

### Permissions and REST equivalents

| Operation      | REST                     | Required scopes |
| -------------- | ------------------------ | --------------- |
| `grids.delete` | `DELETE /grids/{gridId}` | `pools:admin`   |

### Arguments

| Argument  | Type     | Required | Description                                |
| --------- | -------- | -------- | ------------------------------------------ |
| `gridId`  | `string` | Yes      | Grid id                                    |
| `confirm` | `true`   | Yes      | Must be true: this action cannot be undone |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "gridId": {
      "type": "string",
      "minLength": 1,
      "description": "Grid id"
    },
    "confirm": {
      "type": "boolean",
      "const": true,
      "description": "Must be true: this action cannot be undone"
    }
  },
  "required": [
    "gridId",
    "confirm"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Id of the deleted grid
* `deleted` (`true`): See the complete response schema below.

<details>

<summary>Response schema: grids.delete</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Id of the deleted grid"
    },
    "deleted": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "id",
    "deleted"
  ],
  "additionalProperties": false
}
```

</details>


# Servers and orders

Order dedicated machines, follow progress and manage power and replacement.

Order dedicated machines, follow progress and manage power and replacement.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

Use `name` as the server display label and `address` as its stable identifier. `addedBy` identifies who added it, or is null without `members:read`, for another organization’s server or when nobody is recorded. Rented marketplace machines otherwise use hardware, Kinesis region and an address suffix, for example `H100 80GB in US West #a1b2`. Their `hostname`, `csp`, `region`, `zone` and `instanceType` are null; use `kcRegion` for the Kinesis region. Server logs are raw machine output and may contain sensitive data.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `list_servers`

List the organization's servers, those it rents, and pending orders. A shared grid's servers count as usable

**Group:** `servers` · **Behavior:** Read

**Compact tool:** `servers` · **Action:** `list_servers`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation      | REST           | Required scopes |
| -------------- | -------------- | --------------- |
| `servers.list` | `GET /servers` | `pools:read`    |

### Arguments

| Argument | Type      | Required | Description                                                                                      |
| -------- | --------- | -------- | ------------------------------------------------------------------------------------------------ |
| `limit`  | `integer` | No       | Items per page, 1 to 200 (default 50)                                                            |
| `cursor` | `string`  | No       | nextCursor from the previous page; omit for the first page                                       |
| `org`    | `string`  | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 200,
      "description": "Items per page, 1 to 200 (default 50)"
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "nextCursor from the previous page; omit for the first page"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `servers` (`array<object>`): One page of servers, by address
* `nextCursor` (`string or null`): Pass as cursor to fetch the next page; null on the last page
* `pending` (`array<object>`): Provisioning orders that have not produced a server yet; complete on every page

<details>

<summary>Response schema: servers.list</summary>

```json
{
  "type": "object",
  "properties": {
    "servers": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string",
            "description": "Server (node) address"
          },
          "name": {
            "description": "Display name: the caller's own name for the server when set; else, for a server rented from the marketplace, its hardware and Kinesis region; else the hostname",
            "type": [
              "string",
              "null"
            ]
          },
          "hostname": {
            "description": "Null for servers rented from the marketplace",
            "type": [
              "string",
              "null"
            ]
          },
          "csp": {
            "description": "Cloud provider; null for servers rented from the marketplace",
            "type": [
              "string",
              "null"
            ]
          },
          "region": {
            "description": "Provider region; null for servers rented from the marketplace",
            "type": [
              "string",
              "null"
            ]
          },
          "zone": {
            "description": "Provider zone; null for servers rented from the marketplace",
            "type": [
              "string",
              "null"
            ]
          },
          "instanceType": {
            "description": "Provider instance type; null for servers rented from the marketplace",
            "type": [
              "string",
              "null"
            ]
          },
          "kcRegion": {
            "description": "Kinesis region the server serves, e.g. us-west",
            "type": [
              "string",
              "null"
            ]
          },
          "owned": {
            "type": "boolean",
            "description": "The organization owns this server (false for servers seen through a shared grid)"
          },
          "ownerName": {
            "description": "The organization that owns it; null for rented servers",
            "type": [
              "string",
              "null"
            ]
          },
          "addedBy": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string",
                    "description": "User id, as members.list and the sharing calls use it"
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "email": {
                    "type": "string"
                  }
                },
                "required": [
                  "userId",
                  "name",
                  "email"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Who added it to the organization. Null when nobody is on record, for another organization's server, and for a credential without members:read"
          },
          "state": {
            "type": "string"
          },
          "hibernation": {
            "type": "string",
            "description": "active, hibernated, hibernating or waking"
          },
          "lastPulseAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "specs": {
            "type": "object",
            "properties": {
              "cpuThreads": {
                "type": "number"
              },
              "ramBytes": {
                "type": "number"
              },
              "storageBytes": {
                "type": "number"
              },
              "gpus": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "model": {
                      "type": "string"
                    },
                    "count": {
                      "type": "number"
                    },
                    "vramBytes": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "model",
                    "count",
                    "vramBytes"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "required": [
              "cpuThreads",
              "ramBytes",
              "storageBytes",
              "gpus"
            ],
            "additionalProperties": false
          },
          "utilization": {
            "type": "object",
            "properties": {
              "cpu": {
                "type": "number"
              },
              "ram": {
                "type": "number"
              },
              "storage": {
                "type": "number"
              },
              "gpu": {
                "type": "number"
              },
              "vram": {
                "type": "number"
              }
            },
            "required": [
              "cpu",
              "ram",
              "storage",
              "gpu",
              "vram"
            ],
            "additionalProperties": false,
            "description": "Percentages from the last pulse"
          },
          "runningApps": {
            "type": "integer"
          },
          "lease": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "gridId": {
                    "type": "string"
                  },
                  "availableUntil": {
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "date-time",
                        "description": "ISO 8601 timestamp (UTC)"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "priceCentsPerHour": {
                    "type": "number"
                  }
                },
                "required": [
                  "id",
                  "gridId",
                  "availableUntil",
                  "priceCentsPerHour"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Spot lease, for servers rented from the marketplace"
          },
          "provisionStatus": {
            "description": "Provisioning order status while the server is being created",
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "address",
          "name",
          "hostname",
          "csp",
          "region",
          "zone",
          "instanceType",
          "kcRegion",
          "owned",
          "ownerName",
          "addedBy",
          "state",
          "hibernation",
          "lastPulseAt",
          "specs",
          "utilization",
          "runningApps",
          "lease",
          "provisionStatus",
          "createdAt"
        ],
        "additionalProperties": false
      },
      "description": "One page of servers, by address"
    },
    "nextCursor": {
      "description": "Pass as cursor to fetch the next page; null on the last page",
      "type": [
        "string",
        "null"
      ]
    },
    "pending": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "orderId": {
            "type": "string",
            "description": "Provisioning order id"
          },
          "address": {
            "description": "Server address once the machine has registered; null while it is still being created",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "failure": {
            "description": "Failure category when the order is failing",
            "type": [
              "string",
              "null"
            ]
          },
          "csp": {
            "type": [
              "string",
              "null"
            ]
          },
          "region": {
            "type": [
              "string",
              "null"
            ]
          },
          "instanceType": {
            "type": [
              "string",
              "null"
            ]
          },
          "gridIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Grids the server joins once it registers"
          },
          "attempts": {
            "type": "object",
            "properties": {
              "made": {
                "type": "integer"
              },
              "max": {
                "type": "integer"
              }
            },
            "required": [
              "made",
              "max"
            ],
            "additionalProperties": false
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "orderId",
          "address",
          "status",
          "failure",
          "csp",
          "region",
          "instanceType",
          "gridIds",
          "attempts",
          "createdAt"
        ],
        "additionalProperties": false
      },
      "description": "Provisioning orders that have not produced a server yet; complete on every page"
    }
  },
  "required": [
    "servers",
    "nextCursor",
    "pending"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `get_server`

Get one server: specs, utilization, apps, grids and provisioning order. Its own organization also sees its public address

**Group:** `servers` · **Behavior:** Read

**Compact tool:** `servers` · **Action:** `get_server`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation     | REST                     | Required scopes |
| ------------- | ------------------------ | --------------- |
| `servers.get` | `GET /servers/{address}` | `pools:read`    |

### Arguments

| Argument  | Type     | Required | Description                                                                                      |
| --------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `address` | `string` | Yes      | Server address                                                                                   |
| `org`     | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "address": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$",
      "description": "Server address"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "address"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `address` (`string`): Server (node) address
* `name` (`string or null`): Display name: the caller's own name for the server when set; else, for a server rented from the marketplace, its hardware and Kinesis region; else the hostname
* `hostname` (`string or null`): Null for servers rented from the marketplace
* `csp` (`string or null`): Cloud provider; null for servers rented from the marketplace
* `region` (`string or null`): Provider region; null for servers rented from the marketplace
* `zone` (`string or null`): Provider zone; null for servers rented from the marketplace
* `instanceType` (`string or null`): Provider instance type; null for servers rented from the marketplace
* `kcRegion` (`string or null`): Kinesis region the server serves, e.g. us-west
* `owned` (`boolean`): The organization owns this server (false for servers seen through a shared grid)
* `ownerName` (`string or null`): The organization that owns it; null for rented servers
* `addedBy` (`object or null`): Who added it to the organization. Null when nobody is on record, for another organization's server, and for a credential without members:read
* `state` (`string`): See the complete response schema below.
* `hibernation` (`string`): active, hibernated, hibernating or waking
* `lastPulseAt` (`string or null`): See the complete response schema below.
* `specs` (`object`): See the complete response schema below.
* `utilization` (`object`): Percentages from the last pulse
* `runningApps` (`integer`): See the complete response schema below.
* `lease` (`object or null`): Spot lease, for servers rented from the marketplace
* `provisionStatus` (`string or null`): Provisioning order status while the server is being created
* `createdAt` (`string or null`): See the complete response schema below.
* `ipAddress` (`string or null`): Public address; null unless the server belongs to the selected organization
* `version` (`string or null`): Agent version the server runs
* `countryCode` (`string or null`): See the complete response schema below.
* `autoWakeup` (`boolean`): The platform wakes the server when an app needs it
* `gridIds` (`array<string>`): Grids the server belongs to, among those the caller can see
* `apps` (`array<object>`): Apps placed on the server and their container state there
* `order` (`object or null`): The provisioning order behind a Kinesis-provisioned server; null for an attached machine

<details>

<summary>Response schema: servers.get</summary>

```json
{
  "type": "object",
  "properties": {
    "address": {
      "type": "string",
      "description": "Server (node) address"
    },
    "name": {
      "description": "Display name: the caller's own name for the server when set; else, for a server rented from the marketplace, its hardware and Kinesis region; else the hostname",
      "type": [
        "string",
        "null"
      ]
    },
    "hostname": {
      "description": "Null for servers rented from the marketplace",
      "type": [
        "string",
        "null"
      ]
    },
    "csp": {
      "description": "Cloud provider; null for servers rented from the marketplace",
      "type": [
        "string",
        "null"
      ]
    },
    "region": {
      "description": "Provider region; null for servers rented from the marketplace",
      "type": [
        "string",
        "null"
      ]
    },
    "zone": {
      "description": "Provider zone; null for servers rented from the marketplace",
      "type": [
        "string",
        "null"
      ]
    },
    "instanceType": {
      "description": "Provider instance type; null for servers rented from the marketplace",
      "type": [
        "string",
        "null"
      ]
    },
    "kcRegion": {
      "description": "Kinesis region the server serves, e.g. us-west",
      "type": [
        "string",
        "null"
      ]
    },
    "owned": {
      "type": "boolean",
      "description": "The organization owns this server (false for servers seen through a shared grid)"
    },
    "ownerName": {
      "description": "The organization that owns it; null for rented servers",
      "type": [
        "string",
        "null"
      ]
    },
    "addedBy": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "userId": {
              "type": "string",
              "description": "User id, as members.list and the sharing calls use it"
            },
            "name": {
              "type": [
                "string",
                "null"
              ]
            },
            "email": {
              "type": "string"
            }
          },
          "required": [
            "userId",
            "name",
            "email"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Who added it to the organization. Null when nobody is on record, for another organization's server, and for a credential without members:read"
    },
    "state": {
      "type": "string"
    },
    "hibernation": {
      "type": "string",
      "description": "active, hibernated, hibernating or waking"
    },
    "lastPulseAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ]
    },
    "specs": {
      "type": "object",
      "properties": {
        "cpuThreads": {
          "type": "number"
        },
        "ramBytes": {
          "type": "number"
        },
        "storageBytes": {
          "type": "number"
        },
        "gpus": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "model": {
                "type": "string"
              },
              "count": {
                "type": "number"
              },
              "vramBytes": {
                "type": "number"
              }
            },
            "required": [
              "model",
              "count",
              "vramBytes"
            ],
            "additionalProperties": false
          }
        }
      },
      "required": [
        "cpuThreads",
        "ramBytes",
        "storageBytes",
        "gpus"
      ],
      "additionalProperties": false
    },
    "utilization": {
      "type": "object",
      "properties": {
        "cpu": {
          "type": "number"
        },
        "ram": {
          "type": "number"
        },
        "storage": {
          "type": "number"
        },
        "gpu": {
          "type": "number"
        },
        "vram": {
          "type": "number"
        }
      },
      "required": [
        "cpu",
        "ram",
        "storage",
        "gpu",
        "vram"
      ],
      "additionalProperties": false,
      "description": "Percentages from the last pulse"
    },
    "runningApps": {
      "type": "integer"
    },
    "lease": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            },
            "gridId": {
              "type": "string"
            },
            "availableUntil": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time",
                  "description": "ISO 8601 timestamp (UTC)"
                },
                {
                  "type": "null"
                }
              ]
            },
            "priceCentsPerHour": {
              "type": "number"
            }
          },
          "required": [
            "id",
            "gridId",
            "availableUntil",
            "priceCentsPerHour"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Spot lease, for servers rented from the marketplace"
    },
    "provisionStatus": {
      "description": "Provisioning order status while the server is being created",
      "type": [
        "string",
        "null"
      ]
    },
    "createdAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ]
    },
    "ipAddress": {
      "description": "Public address; null unless the server belongs to the selected organization",
      "type": [
        "string",
        "null"
      ]
    },
    "version": {
      "description": "Agent version the server runs",
      "type": [
        "string",
        "null"
      ]
    },
    "countryCode": {
      "type": [
        "string",
        "null"
      ]
    },
    "autoWakeup": {
      "type": "boolean",
      "description": "The platform wakes the server when an app needs it"
    },
    "gridIds": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Grids the server belongs to, among those the caller can see"
    },
    "apps": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "guid": {
            "type": "string",
            "description": "Runtime identifier of the app (list_apps shows it as guid)"
          },
          "image": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": "string",
            "description": "Container state on this server"
          },
          "targetState": {
            "type": "string",
            "enum": [
              "running",
              "stopped",
              "unknown"
            ]
          },
          "reportedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "guid",
          "image",
          "state",
          "targetState",
          "reportedAt"
        ],
        "additionalProperties": false
      },
      "description": "Apps placed on the server and their container state there"
    },
    "order": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "id": {
              "type": "string",
              "description": "Provisioning order id"
            },
            "status": {
              "type": "string"
            },
            "failure": {
              "type": [
                "string",
                "null"
              ]
            },
            "attempts": {
              "type": "object",
              "properties": {
                "made": {
                  "type": "integer"
                },
                "max": {
                  "type": "integer"
                }
              },
              "required": [
                "made",
                "max"
              ],
              "additionalProperties": false
            },
            "awaitingStockSince": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time",
                  "description": "ISO 8601 timestamp (UTC)"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Set while a wake waits for provider stock"
            }
          },
          "required": [
            "id",
            "status",
            "failure",
            "attempts",
            "awaitingStockSince"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "The provisioning order behind a Kinesis-provisioned server; null for an attached machine"
    }
  },
  "required": [
    "address",
    "name",
    "hostname",
    "csp",
    "region",
    "zone",
    "instanceType",
    "kcRegion",
    "owned",
    "ownerName",
    "addedBy",
    "state",
    "hibernation",
    "lastPulseAt",
    "specs",
    "utilization",
    "runningApps",
    "lease",
    "provisionStatus",
    "createdAt",
    "ipAddress",
    "version",
    "countryCode",
    "autoWakeup",
    "gridIds",
    "apps",
    "order"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `get_server_logs`

Read the newest lines of a server's own logs. Agent, runtime health, installation and GPU setup, for one of the organization's servers. Narrow the window with since and until; lines come back oldest to newest. They are the machine's logs as written and may include sensitive data.

**Group:** `servers` · **Behavior:** Read

**Compact tool:** `servers` · **Action:** `get_server_logs`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation      | REST                          | Required scopes   |
| -------------- | ----------------------------- | ----------------- |
| `servers.logs` | `GET /servers/{address}/logs` | `monitoring:read` |

### Arguments

| Argument  | Type      | Required | Description                                                                                      |
| --------- | --------- | -------- | ------------------------------------------------------------------------------------------------ |
| `address` | `string`  | Yes      | Server address                                                                                   |
| `since`   | `string`  | No       | Earliest line to include; omit for no lower bound                                                |
| `until`   | `string`  | No       | Latest line to include; omit for now                                                             |
| `limit`   | `integer` | No       | Newest lines to return within the window, 500 by default                                         |
| `org`     | `string`  | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "address": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$",
      "description": "Server address"
    },
    "since": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
      "description": "Earliest line to include; omit for no lower bound"
    },
    "until": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
      "description": "Latest line to include; omit for now"
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 5000,
      "description": "Newest lines to return within the window, 500 by default"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "address"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `logs` (`array<object>`): Oldest to newest; the last line is the most recent
* `truncated` (`boolean`): The window holds more lines than limit; the oldest were dropped (pass until to read further back)

<details>

<summary>Response schema: servers.logs</summary>

```json
{
  "type": "object",
  "properties": {
    "logs": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          },
          "source": {
            "type": "string",
            "description": "Component that wrote the line, e.g. the agent, the runtime, GPU setup"
          },
          "level": {
            "type": "string",
            "enum": [
              "trace",
              "info",
              "warning",
              "error",
              "unknown"
            ]
          },
          "text": {
            "type": "string"
          }
        },
        "required": [
          "at",
          "source",
          "level",
          "text"
        ],
        "additionalProperties": false
      },
      "description": "Oldest to newest; the last line is the most recent"
    },
    "truncated": {
      "type": "boolean",
      "description": "The window holds more lines than limit; the oldest were dropped (pass until to read further back)"
    }
  },
  "required": [
    "logs",
    "truncated"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `provision_servers`

Order dedicated servers from a cloud provider. Billing starts once they come up. Checks the quota and the balance first. Set dryRun to get the cost estimate without ordering; use browse\_catalog for the region and instance type ids. Returns an order; poll get\_provision\_order until done.

**Group:** `servers` · **Behavior:** Write

**Compact tool:** `servers_write` · **Action:** `provision_servers`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation           | REST                      | Required scopes                |
| ------------------- | ------------------------- | ------------------------------ |
| `servers.provision` | `POST /servers/provision` | `pools:write`, `compute:spend` |

### Arguments

| Argument          | Type                          | Required | Description                                                                                                                                                           |
| ----------------- | ----------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `csp`             | `"kinesis" / "aws" / "azure"` | Yes      | Cloud provider                                                                                                                                                        |
| `region`          | `string`                      | Yes      | Provider region id (see catalog.regions)                                                                                                                              |
| `zone`            | `string`                      | No       | Availability zone; omit to let the provider choose                                                                                                                    |
| `instanceType`    | `string`                      | Yes      | Instance type id (see catalog.instanceTypes)                                                                                                                          |
| `count`           | `integer`                     | No       | Servers to order, 1 to 10 (default 1)                                                                                                                                 |
| `storageSizeGb`   | `integer`                     | No       | Disk per server in GB, 50 to 10000; required unless useEphemeralSsd is set                                                                                            |
| `useEphemeralSsd` | `boolean`                     | No       | Use the instance's bundled local SSD instead of a network volume (Kinesis compute's large GPU instance types that can't hibernate only)                               |
| `gridIds`         | `array<string>`               | No       | Grids the servers join once they register                                                                                                                             |
| `dryRun`          | `boolean`                     | No       | Run every check and return the estimate without ordering                                                                                                              |
| `idempotencyKey`  | `string`                      | No       | Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice |
| `org`             | `string`                      | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one                                                                      |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "csp": {
      "type": "string",
      "enum": [
        "kinesis",
        "aws",
        "azure"
      ],
      "description": "Cloud provider"
    },
    "region": {
      "type": "string",
      "minLength": 1,
      "description": "Provider region id (see catalog.regions)"
    },
    "zone": {
      "type": "string",
      "minLength": 1,
      "description": "Availability zone; omit to let the provider choose"
    },
    "instanceType": {
      "type": "string",
      "minLength": 1,
      "description": "Instance type id (see catalog.instanceTypes)"
    },
    "count": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10,
      "description": "Servers to order, 1 to 10 (default 1)"
    },
    "storageSizeGb": {
      "type": "integer",
      "minimum": 50,
      "maximum": 10000,
      "description": "Disk per server in GB, 50 to 10000; required unless useEphemeralSsd is set"
    },
    "useEphemeralSsd": {
      "type": "boolean",
      "description": "Use the instance's bundled local SSD instead of a network volume (Kinesis compute's large GPU instance types that can't hibernate only)"
    },
    "gridIds": {
      "maxItems": 10,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "description": "Grids the servers join once they register"
    },
    "dryRun": {
      "type": "boolean",
      "description": "Run every check and return the estimate without ordering"
    },
    "idempotencyKey": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "pattern": "^\\S+$",
      "description": "Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "csp",
    "region",
    "instanceType"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `dryRun` (`boolean`): See the complete response schema below.
* `csp` (`string`): See the complete response schema below.
* `region` (`string`): See the complete response schema below.
* `instanceType` (`string`): See the complete response schema below.
* `count` (`integer`): See the complete response schema below.
* `estimate` (`object`): See the complete response schema below.
* `orders` (`array<object>`): Empty for a dry run

<details>

<summary>Response schema: servers.provision</summary>

```json
{
  "type": "object",
  "properties": {
    "dryRun": {
      "type": "boolean"
    },
    "csp": {
      "type": "string"
    },
    "region": {
      "type": "string"
    },
    "instanceType": {
      "type": "string"
    },
    "count": {
      "type": "integer"
    },
    "estimate": {
      "type": "object",
      "properties": {
        "instanceCount": {
          "type": "integer"
        },
        "totalHourlyCostCents": {
          "type": "number",
          "description": "Hourly cost of the whole order"
        },
        "totalRequiredCents": {
          "type": "number",
          "description": "Balance needed to cover the minimum runtime"
        },
        "minimumHours": {
          "type": "number",
          "description": "Hours of runtime the balance must cover before an order is accepted"
        },
        "balanceCents": {
          "type": "number"
        },
        "overLimitCents": {
          "type": "number"
        },
        "availableRunwayCents": {
          "type": "number",
          "description": "Balance plus the over-limit allowance"
        },
        "topUpNeededCents": {
          "type": "number",
          "description": "0 when the balance suffices"
        }
      },
      "required": [
        "instanceCount",
        "totalHourlyCostCents",
        "totalRequiredCents",
        "minimumHours",
        "balanceCents",
        "overLimitCents",
        "availableRunwayCents",
        "topUpNeededCents"
      ],
      "additionalProperties": false
    },
    "orders": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "orderId": {
            "type": "string",
            "description": "Provisioning order id"
          },
          "status": {
            "type": "string"
          },
          "count": {
            "type": "integer",
            "description": "Servers in the order"
          },
          "csp": {
            "type": [
              "string",
              "null"
            ]
          },
          "failure": {
            "description": "Failure category when the order is failing",
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          },
          "dismissed": {
            "type": "boolean",
            "description": "Hidden from the pending list (servers.orders.dismiss)"
          }
        },
        "required": [
          "orderId",
          "status",
          "count",
          "csp",
          "failure",
          "createdAt",
          "updatedAt",
          "dismissed"
        ],
        "additionalProperties": false
      },
      "description": "Empty for a dry run"
    }
  },
  "required": [
    "dryRun",
    "csp",
    "region",
    "instanceType",
    "count",
    "estimate",
    "orders"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `402 insufficient_balance`, `403 provision_quota`, `400 no_provider_for_region`, `400 ephemeral_ssd_unavailable`, `400 org_required`, `403 key_project_restricted`.

## `deprovision_servers`

Destroy dedicated servers. They leave every grid first. Irreversible.

**Group:** `servers` · **Behavior:** Write

**Compact tool:** `servers_write` · **Action:** `deprovision_servers`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

Requires `confirm: true` for this irreversible action.

### Permissions and REST equivalents

| Operation             | REST                        | Required scopes |
| --------------------- | --------------------------- | --------------- |
| `servers.deprovision` | `POST /servers/deprovision` | `pools:write`   |

### Arguments

| Argument    | Type            | Required | Description                                                                                      |
| ----------- | --------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `addresses` | `array<string>` | Yes      | Server addresses, at most 50                                                                     |
| `confirm`   | `true`          | Yes      | Must be true: this action cannot be undone                                                       |
| `org`       | `string`        | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "addresses": {
      "minItems": 1,
      "maxItems": 50,
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^0x[0-9a-fA-F]{40}$"
      },
      "description": "Server addresses, at most 50"
    },
    "confirm": {
      "type": "boolean",
      "const": true,
      "description": "Must be true: this action cannot be undone"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "addresses",
    "confirm"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `results` (`array<object>`): One entry per requested server

<details>

<summary>Response schema: servers.deprovision</summary>

```json
{
  "type": "object",
  "properties": {
    "results": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "ok": {
            "type": "boolean"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "address",
          "ok",
          "error"
        ],
        "additionalProperties": false
      },
      "description": "One entry per requested server"
    }
  },
  "required": [
    "results"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `power_servers`

Change the power state of the organization’s servers (admins). hibernate stops the machine and its compute billing but keeps its disk; wake starts a hibernated machine again (fails with out\_of\_stock when the provider has no capacity; retry later); reboot restarts a running machine.

**Group:** `servers` · **Behavior:** Write

**Compact tool:** `servers_write` · **Action:** `power_servers`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation           | REST                      | Required scopes                |
| ------------------- | ------------------------- | ------------------------------ |
| `servers.hibernate` | `POST /servers/hibernate` | `pools:write`                  |
| `servers.wake`      | `POST /servers/wake`      | `pools:write`, `compute:spend` |
| `servers.reboot`    | `POST /servers/reboot`    | `pools:write`                  |

### Arguments

| Argument    | Type                              | Required | Description                                                                                      |
| ----------- | --------------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `action`    | `"hibernate" / "wake" / "reboot"` | Yes      | Power action to apply                                                                            |
| `addresses` | `array<string>`                   | Yes      | Server addresses, at most 50                                                                     |
| `org`       | `string`                          | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "hibernate",
        "wake",
        "reboot"
      ],
      "description": "Power action to apply"
    },
    "addresses": {
      "minItems": 1,
      "maxItems": 50,
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^0x[0-9a-fA-F]{40}$"
      },
      "description": "Server addresses, at most 50"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "action",
    "addresses"
  ],
  "additionalProperties": false
}
```

</details>

### Result

**`servers.hibernate`**

* `results` (`array<object>`): One entry per requested server; ok means the request was accepted, the state change is asynchronous

<details>

<summary>Response schema: servers.hibernate</summary>

```json
{
  "type": "object",
  "properties": {
    "results": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "ok": {
            "type": "boolean"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "reason": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "out_of_stock",
                  "not_hibernatable",
                  "not_active",
                  "unknown"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Why the request was refused or failed: out_of_stock means retry later; not_hibernatable and not_active are screened before the backend is asked"
          }
        },
        "required": [
          "address",
          "ok",
          "error",
          "reason"
        ],
        "additionalProperties": false
      },
      "description": "One entry per requested server; ok means the request was accepted, the state change is asynchronous"
    }
  },
  "required": [
    "results"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`. **`servers.wake`**

* `results` (`array<object>`): One entry per requested server; ok means the request was accepted, the state change is asynchronous

<details>

<summary>Response schema: servers.wake</summary>

```json
{
  "type": "object",
  "properties": {
    "results": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "ok": {
            "type": "boolean"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "reason": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "out_of_stock",
                  "not_hibernatable",
                  "not_active",
                  "unknown"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Why the request was refused or failed: out_of_stock means retry later; not_hibernatable and not_active are screened before the backend is asked"
          }
        },
        "required": [
          "address",
          "ok",
          "error",
          "reason"
        ],
        "additionalProperties": false
      },
      "description": "One entry per requested server; ok means the request was accepted, the state change is asynchronous"
    }
  },
  "required": [
    "results"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`. **`servers.reboot`**

* `results` (`array<object>`): One entry per requested server

<details>

<summary>Response schema: servers.reboot</summary>

```json
{
  "type": "object",
  "properties": {
    "results": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "ok": {
            "type": "boolean"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "address",
          "ok",
          "error"
        ],
        "additionalProperties": false
      },
      "description": "One entry per requested server"
    }
  },
  "required": [
    "results"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `replace_server`

Provision an identical server and retire this one once the new one is up. The retired server's disk is not kept. Poll servers.orders.get with the returned order id to follow the replacement.

**Group:** `servers` · **Behavior:** Write

**Compact tool:** `servers_write` · **Action:** `replace_server`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

Requires `confirm: true` for this irreversible action.

### Permissions and REST equivalents

| Operation         | REST                              | Required scopes                |
| ----------------- | --------------------------------- | ------------------------------ |
| `servers.replace` | `POST /servers/{address}/replace` | `pools:write`, `compute:spend` |

### Arguments

| Argument         | Type     | Required | Description                                                                                                                                                           |
| ---------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `address`        | `string` | Yes      | Address of the server to replace                                                                                                                                      |
| `confirm`        | `true`   | Yes      | Must be true: this action cannot be undone                                                                                                                            |
| `idempotencyKey` | `string` | No       | Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice |
| `org`            | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one                                                                      |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "address": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$",
      "description": "Address of the server to replace"
    },
    "confirm": {
      "type": "boolean",
      "const": true,
      "description": "Must be true: this action cannot be undone"
    },
    "idempotencyKey": {
      "type": "string",
      "minLength": 1,
      "maxLength": 255,
      "pattern": "^\\S+$",
      "description": "Unique string without whitespace; send the same one with the same arguments when retrying after a timeout, and the original result comes back instead of acting twice"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "address",
    "confirm"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `order` (`object or null`): The resulting order, when the backend returned one

<details>

<summary>Response schema: servers.replace</summary>

```json
{
  "type": "object",
  "properties": {
    "order": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "orderId": {
              "type": "string",
              "description": "Provisioning order id"
            },
            "status": {
              "type": "string"
            },
            "count": {
              "type": "integer",
              "description": "Servers in the order"
            },
            "csp": {
              "type": [
                "string",
                "null"
              ]
            },
            "failure": {
              "description": "Failure category when the order is failing",
              "type": [
                "string",
                "null"
              ]
            },
            "createdAt": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time",
                  "description": "ISO 8601 timestamp (UTC)"
                },
                {
                  "type": "null"
                }
              ]
            },
            "updatedAt": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time",
                  "description": "ISO 8601 timestamp (UTC)"
                },
                {
                  "type": "null"
                }
              ]
            },
            "dismissed": {
              "type": "boolean",
              "description": "Hidden from the pending list (servers.orders.dismiss)"
            }
          },
          "required": [
            "orderId",
            "status",
            "count",
            "csp",
            "failure",
            "createdAt",
            "updatedAt",
            "dismissed"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "The resulting order, when the backend returned one"
    }
  },
  "required": [
    "order"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `get_provision_order`

Get a provisioning order the caller placed and the machines it has produced. Completed, replacement and dismissed orders are readable too. Poll this after servers.provision or servers.replace until done.

**Group:** `servers` · **Behavior:** Read

**Compact tool:** `servers` · **Action:** `get_provision_order`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation            | REST                            | Required scopes |
| -------------------- | ------------------------------- | --------------- |
| `servers.orders.get` | `GET /servers/orders/{orderId}` | `pools:read`    |

### Arguments

| Argument  | Type     | Required | Description                                                                                      |
| --------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `orderId` | `string` | Yes      | Provisioning order id                                                                            |
| `org`     | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "orderId": {
      "type": "string",
      "minLength": 1,
      "description": "Provisioning order id"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "orderId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `order` (`object`): See the complete response schema below.
* `instances` (`array<object>`): One entry per machine in the order; address is set once the machine has registered
* `done` (`boolean`): The order reached a terminal state: complete, failed or cancelled

<details>

<summary>Response schema: servers.orders.get</summary>

```json
{
  "type": "object",
  "properties": {
    "order": {
      "type": "object",
      "properties": {
        "orderId": {
          "type": "string",
          "description": "Provisioning order id"
        },
        "status": {
          "type": "string"
        },
        "count": {
          "type": "integer",
          "description": "Servers in the order"
        },
        "csp": {
          "type": [
            "string",
            "null"
          ]
        },
        "failure": {
          "description": "Failure category when the order is failing",
          "type": [
            "string",
            "null"
          ]
        },
        "createdAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 timestamp (UTC)"
            },
            {
              "type": "null"
            }
          ]
        },
        "updatedAt": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time",
              "description": "ISO 8601 timestamp (UTC)"
            },
            {
              "type": "null"
            }
          ]
        },
        "dismissed": {
          "type": "boolean",
          "description": "Hidden from the pending list (servers.orders.dismiss)"
        }
      },
      "required": [
        "orderId",
        "status",
        "count",
        "csp",
        "failure",
        "createdAt",
        "updatedAt",
        "dismissed"
      ],
      "additionalProperties": false
    },
    "instances": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "orderId": {
            "type": "string",
            "description": "Provisioning order id"
          },
          "address": {
            "description": "Server address once the machine has registered; null while it is still being created",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "failure": {
            "description": "Failure category when the order is failing",
            "type": [
              "string",
              "null"
            ]
          },
          "csp": {
            "type": [
              "string",
              "null"
            ]
          },
          "region": {
            "type": [
              "string",
              "null"
            ]
          },
          "instanceType": {
            "type": [
              "string",
              "null"
            ]
          },
          "gridIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Grids the server joins once it registers"
          },
          "attempts": {
            "type": "object",
            "properties": {
              "made": {
                "type": "integer"
              },
              "max": {
                "type": "integer"
              }
            },
            "required": [
              "made",
              "max"
            ],
            "additionalProperties": false
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "orderId",
          "address",
          "status",
          "failure",
          "csp",
          "region",
          "instanceType",
          "gridIds",
          "attempts",
          "createdAt"
        ],
        "additionalProperties": false
      },
      "description": "One entry per machine in the order; address is set once the machine has registered"
    },
    "done": {
      "type": "boolean",
      "description": "The order reached a terminal state: complete, failed or cancelled"
    }
  },
  "required": [
    "order",
    "instances",
    "done"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `manage_provision_order`

Retry a failed provisioning order, or dismiss one. retry re-runs a failed order (see get\_provision\_order, or list\_servers under pending); dismiss cancels an in-flight order or hides a finished one from the pending list.

**Group:** `servers` · **Behavior:** Write

**Compact tool:** `servers_write` · **Action:** `manage_provision_order`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                | REST                                     | Required scopes                |
| ------------------------ | ---------------------------------------- | ------------------------------ |
| `servers.orders.retry`   | `POST /servers/orders/{orderId}/retry`   | `pools:write`, `compute:spend` |
| `servers.orders.dismiss` | `POST /servers/orders/{orderId}/dismiss` | `pools:write`                  |

### Arguments

| Argument  | Type                  | Required | Description                                                                                      |
| --------- | --------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `action`  | `"retry" / "dismiss"` | Yes      | What to do with the order                                                                        |
| `orderId` | `string`              | Yes      | Provisioning order id                                                                            |
| `org`     | `string`              | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "retry",
        "dismiss"
      ],
      "description": "What to do with the order"
    },
    "orderId": {
      "type": "string",
      "minLength": 1,
      "description": "Provisioning order id"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "action",
    "orderId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

**`servers.orders.retry`**

* `order` (`object or null`): The resulting order, when the backend returned one

<details>

<summary>Response schema: servers.orders.retry</summary>

```json
{
  "type": "object",
  "properties": {
    "order": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "orderId": {
              "type": "string",
              "description": "Provisioning order id"
            },
            "status": {
              "type": "string"
            },
            "count": {
              "type": "integer",
              "description": "Servers in the order"
            },
            "csp": {
              "type": [
                "string",
                "null"
              ]
            },
            "failure": {
              "description": "Failure category when the order is failing",
              "type": [
                "string",
                "null"
              ]
            },
            "createdAt": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time",
                  "description": "ISO 8601 timestamp (UTC)"
                },
                {
                  "type": "null"
                }
              ]
            },
            "updatedAt": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time",
                  "description": "ISO 8601 timestamp (UTC)"
                },
                {
                  "type": "null"
                }
              ]
            },
            "dismissed": {
              "type": "boolean",
              "description": "Hidden from the pending list (servers.orders.dismiss)"
            }
          },
          "required": [
            "orderId",
            "status",
            "count",
            "csp",
            "failure",
            "createdAt",
            "updatedAt",
            "dismissed"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "The resulting order, when the backend returned one"
    }
  },
  "required": [
    "order"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 provision_order_failed`, `400 org_required`, `403 key_project_restricted`. **`servers.orders.dismiss`**

* `orderId` (`string`): See the complete response schema below.
* `dismissed` (`true`): See the complete response schema below.

<details>

<summary>Response schema: servers.orders.dismiss</summary>

```json
{
  "type": "object",
  "properties": {
    "orderId": {
      "type": "string"
    },
    "dismissed": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "orderId",
    "dismissed"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `409 provision_order_failed`, `400 org_required`, `403 key_project_restricted`.


# Server setup and SSH

Attach a machine, configure Smart Lifecycle and manage SSH public keys.

Attach a machine, configure Smart Lifecycle and manage SSH public keys.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

The public BYOD action registers through Kinesis Remote Reach; its response retains the wire field `needVpn`. It accepts token expiry and renewal options, not connection-mode or SSH-management options. Portal onboarding offers a separate network guide. Install SSH keys only on the `eligibleServers` returned by `list_ssh_keys`; Remote Reach machines and rented marketplace machines are ineligible.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `get_byod_token`

Get a provision token and install command for attaching a machine. The one-line command attaches it (bring your own device) as one of the organization's servers. The same usable token comes back on repeated calls until it is consumed or expires; forceNew mints a fresh one.

**Group:** `servers` · **Behavior:** Write

**Compact tool:** `servers_write` · **Action:** `get_byod_token`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation           | REST                       | Required scopes |
| ------------------- | -------------------------- | --------------- |
| `servers.byodToken` | `POST /servers/byod-token` | `pools:write`   |

### Arguments

| Argument       | Type      | Required | Description                                                                                      |
| -------------- | --------- | -------- | ------------------------------------------------------------------------------------------------ |
| `expiresInSec` | `integer` | No       | Lifetime of a newly minted token, 3600 by default                                                |
| `forceNew`     | `boolean` | No       | Mint a new token even when a usable one exists                                                   |
| `org`          | `string`  | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "expiresInSec": {
      "type": "integer",
      "minimum": 60,
      "maximum": 604800,
      "description": "Lifetime of a newly minted token, 3600 by default"
    },
    "forceNew": {
      "type": "boolean",
      "description": "Mint a new token even when a usable one exists"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

Not available with `profile=openai`; use the portal for this operation.

### Result

* `token` (`string`): Provision token; single use
* `expiresAt` (`string or null`): See the complete response schema below.
* `needVpn` (`boolean`): The machine joins through Kinesis Remote Reach (VPN-backed)
* `expiresInSec` (`integer`): See the complete response schema below.
* `installCommand` (`string`): Run this on the machine as root; it installs the agent and registers it

<details>

<summary>Response schema: servers.byodToken</summary>

```json
{
  "type": "object",
  "properties": {
    "token": {
      "type": "string",
      "description": "Provision token; single use"
    },
    "expiresAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ]
    },
    "needVpn": {
      "type": "boolean",
      "description": "The machine joins through Kinesis Remote Reach (VPN-backed)"
    },
    "expiresInSec": {
      "type": "integer"
    },
    "installCommand": {
      "type": "string",
      "description": "Run this on the machine as root; it installs the agent and registers it"
    }
  },
  "required": [
    "token",
    "expiresAt",
    "needVpn",
    "expiresInSec",
    "installCommand"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `list_smart_lifecycle`

The Smart Lifecycle setting of each server the organization owns. On, the platform hibernates it when nothing runs on it (storage billing only) and wakes it when an app needs it

**Group:** `servers` · **Behavior:** Read

**Compact tool:** `servers` · **Action:** `list_smart_lifecycle`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                     | REST                           | Required scopes |
| ----------------------------- | ------------------------------ | --------------- |
| `servers.smartLifecycle.list` | `GET /servers/smart-lifecycle` | `pools:read`    |

### Arguments

| Argument | Type     | Required | Description                                                                                      |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `org`    | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `servers` (`array<object>`): One entry per server the organization owns

<details>

<summary>Response schema: servers.smartLifecycle.list</summary>

```json
{
  "type": "object",
  "properties": {
    "servers": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean",
            "description": "The platform hibernates the server when idle and wakes it when an app needs it"
          }
        },
        "required": [
          "address",
          "enabled"
        ],
        "additionalProperties": false
      },
      "description": "One entry per server the organization owns"
    }
  },
  "required": [
    "servers"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `set_smart_lifecycle`

Turn automatic hibernate and wake-up (Smart Lifecycle) on or off. For the org's servers

**Group:** `servers` · **Behavior:** Write

**Compact tool:** `servers_write` · **Action:** `set_smart_lifecycle`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                       | REST                             | Required scopes |
| ------------------------------- | -------------------------------- | --------------- |
| `servers.smartLifecycle.update` | `PATCH /servers/smart-lifecycle` | `pools:write`   |

### Arguments

| Argument    | Type            | Required | Description                                                                                      |
| ----------- | --------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `addresses` | `array<string>` | Yes      | Server addresses, at most 50                                                                     |
| `enabled`   | `boolean`       | Yes      | On or off                                                                                        |
| `org`       | `string`        | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "addresses": {
      "minItems": 1,
      "maxItems": 50,
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^0x[0-9a-fA-F]{40}$"
      },
      "description": "Server addresses, at most 50"
    },
    "enabled": {
      "type": "boolean",
      "description": "On or off"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "addresses",
    "enabled"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `servers` (`array<object>`): One entry per server the organization owns

<details>

<summary>Response schema: servers.smartLifecycle.update</summary>

```json
{
  "type": "object",
  "properties": {
    "servers": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean",
            "description": "The platform hibernates the server when idle and wakes it when an app needs it"
          }
        },
        "required": [
          "address",
          "enabled"
        ],
        "additionalProperties": false
      },
      "description": "One entry per server the organization owns"
    }
  },
  "required": [
    "servers"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `list_ssh_keys`

The organization's SSH public keys and where each is installed (admins). Also its servers a key can be installed on (those added without Kinesis Remote Reach)

**Group:** `servers` · **Behavior:** Read

**Compact tool:** `servers` · **Action:** `list_ssh_keys`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation      | REST            | Required scopes |
| -------------- | --------------- | --------------- |
| `sshKeys.list` | `GET /ssh-keys` | `pools:read`    |

### Arguments

| Argument | Type     | Required | Description                                                                                      |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `org`    | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `keys` (`array<object>`): See the complete response schema below.
* `eligibleServers` (`array<object>`): Servers a key can be assigned to

<details>

<summary>Response schema: sshKeys.list</summary>

```json
{
  "type": "object",
  "properties": {
    "keys": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Key id"
          },
          "label": {
            "type": "string"
          },
          "fingerprint": {
            "type": "string"
          },
          "servers": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Addresses of the servers the key is installed on"
          },
          "createdAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "ISO 8601 timestamp (UTC)"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "label",
          "fingerprint",
          "servers",
          "createdAt"
        ],
        "additionalProperties": false
      }
    },
    "eligibleServers": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string"
          },
          "csp": {
            "type": [
              "string",
              "null"
            ]
          },
          "region": {
            "type": [
              "string",
              "null"
            ]
          },
          "instanceType": {
            "type": [
              "string",
              "null"
            ]
          },
          "ipAddress": {
            "description": "What ssh connects to",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "address",
          "csp",
          "region",
          "instanceType",
          "ipAddress"
        ],
        "additionalProperties": false
      },
      "description": "Servers a key can be assigned to"
    }
  },
  "required": [
    "keys",
    "eligibleServers"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `add_ssh_key`

Register an SSH public key under a label. OpenSSH format; assign it to servers afterwards

**Group:** `servers` · **Behavior:** Write

**Compact tool:** `servers_write` · **Action:** `add_ssh_key`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: false` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation        | REST             | Required scopes |
| ---------------- | ---------------- | --------------- |
| `sshKeys.create` | `POST /ssh-keys` | `pools:write`   |

### Arguments

| Argument    | Type     | Required | Description                                                                                      |
| ----------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `label`     | `string` | Yes      | A name for the key                                                                               |
| `publicKey` | `string` | Yes      | The public key, e.g. the contents of id\_ed25519.pub                                             |
| `org`       | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "label": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "description": "A name for the key"
    },
    "publicKey": {
      "type": "string",
      "minLength": 1,
      "maxLength": 16384,
      "description": "The public key, e.g. the contents of id_ed25519.pub"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "label",
    "publicKey"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `id` (`string`): Key id
* `label` (`string`): See the complete response schema below.
* `fingerprint` (`string`): See the complete response schema below.
* `servers` (`array<string>`): Addresses of the servers the key is installed on
* `createdAt` (`string or null`): See the complete response schema below.

<details>

<summary>Response schema: sshKeys.create</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Key id"
    },
    "label": {
      "type": "string"
    },
    "fingerprint": {
      "type": "string"
    },
    "servers": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Addresses of the servers the key is installed on"
    },
    "createdAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "id",
    "label",
    "fingerprint",
    "servers",
    "createdAt"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `remove_ssh_key`

Delete an SSH key; it is removed from every server it was installed on

**Group:** `servers` · **Behavior:** Write

**Compact tool:** `servers_write` · **Action:** `remove_ssh_key`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation        | REST                       | Required scopes |
| ---------------- | -------------------------- | --------------- |
| `sshKeys.delete` | `DELETE /ssh-keys/{keyId}` | `pools:write`   |

### Arguments

| Argument | Type     | Required | Description                                                                                      |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `keyId`  | `string` | Yes      | Key id from the list                                                                             |
| `org`    | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "keyId": {
      "type": "string",
      "minLength": 1,
      "description": "Key id from the list"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "keyId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `deleted` (`true`): See the complete response schema below.

<details>

<summary>Response schema: sshKeys.delete</summary>

```json
{
  "type": "object",
  "properties": {
    "deleted": {
      "type": "boolean",
      "const": true
    }
  },
  "required": [
    "deleted"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `assign_ssh_key`

Install an organization SSH key on eligible servers, or remove it (admins). action=assign installs it (on the servers list\_ssh\_keys names under eligibleServers); action=unassign removes it from them. ssh @ then works with the matching private key.

**Group:** `servers` · **Behavior:** Write

**Compact tool:** `servers_write` · **Action:** `assign_ssh_key`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation          | REST                               | Required scopes |
| ------------------ | ---------------------------------- | --------------- |
| `sshKeys.assign`   | `POST /ssh-keys/{keyId}/servers`   | `pools:write`   |
| `sshKeys.unassign` | `DELETE /ssh-keys/{keyId}/servers` | `pools:write`   |

### Arguments

| Argument    | Type                    | Required | Description                                                                                      |
| ----------- | ----------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `action`    | `"assign" / "unassign"` | Yes      | Install or remove                                                                                |
| `keyId`     | `string`                | Yes      | Key id from the list                                                                             |
| `addresses` | `array<string>`         | Yes      | Server addresses, at most 50                                                                     |
| `org`       | `string`                | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "action": {
      "type": "string",
      "enum": [
        "assign",
        "unassign"
      ],
      "description": "Install or remove"
    },
    "keyId": {
      "type": "string",
      "minLength": 1,
      "description": "Key id from the list"
    },
    "addresses": {
      "minItems": 1,
      "maxItems": 50,
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^0x[0-9a-fA-F]{40}$"
      },
      "description": "Server addresses, at most 50"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "action",
    "keyId",
    "addresses"
  ],
  "additionalProperties": false
}
```

</details>

### Result

**`sshKeys.assign`**

* `id` (`string`): Key id
* `label` (`string`): See the complete response schema below.
* `fingerprint` (`string`): See the complete response schema below.
* `servers` (`array<string>`): Addresses of the servers the key is installed on
* `createdAt` (`string or null`): See the complete response schema below.

<details>

<summary>Response schema: sshKeys.assign</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Key id"
    },
    "label": {
      "type": "string"
    },
    "fingerprint": {
      "type": "string"
    },
    "servers": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Addresses of the servers the key is installed on"
    },
    "createdAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "id",
    "label",
    "fingerprint",
    "servers",
    "createdAt"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`. **`sshKeys.unassign`**

* `id` (`string`): Key id
* `label` (`string`): See the complete response schema below.
* `fingerprint` (`string`): See the complete response schema below.
* `servers` (`array<string>`): Addresses of the servers the key is installed on
* `createdAt` (`string or null`): See the complete response schema below.

<details>

<summary>Response schema: sshKeys.unassign</summary>

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "Key id"
    },
    "label": {
      "type": "string"
    },
    "fingerprint": {
      "type": "string"
    },
    "servers": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Addresses of the servers the key is installed on"
    },
    "createdAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "id",
    "label",
    "fingerprint",
    "servers",
    "createdAt"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.


# Catalog and marketplace

Find provider regions and instance types, or browse marketplace capacity.

Find provider regions and instance types, or browse marketplace capacity.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `browse_catalog`

What a cloud provider can provision: regions, zones and instance types. kind=regions lists regions (with the default one), kind=zones the availability zones of a region, kind=instance\_types the instance types of a region (popular picks by default, a search, or everything with all=true). Ids from here feed provision\_servers.

**Group:** `catalog` · **Behavior:** Read

**Compact tool:** `catalog` · **Action:** `browse_catalog`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation               | REST                                                 | Required scopes |
| ----------------------- | ---------------------------------------------------- | --------------- |
| `catalog.regions`       | `GET /catalog/{csp}/regions`                         | `pools:read`    |
| `catalog.zones`         | `GET /catalog/{csp}/regions/{region}/zones`          | `pools:read`    |
| `catalog.instanceTypes` | `GET /catalog/{csp}/regions/{region}/instance-types` | `pools:read`    |

### Arguments

| Argument | Type                                     | Required | Description                                               |
| -------- | ---------------------------------------- | -------- | --------------------------------------------------------- |
| `kind`   | `"regions" / "zones" / "instance_types"` | Yes      | Which part of the catalog to list                         |
| `csp`    | `"kinesis" / "aws" / "azure"`            | Yes      | Cloud provider                                            |
| `region` | `string`                                 | No       | Provider region id; required for zones and instance types |
| `zone`   | `string`                                 | No       | Limit to one availability zone                            |
| `search` | `string`                                 | No       | Substring to search names and GPU types for               |
| `all`    | `boolean`                                | No       | List the full catalog instead of the popular picks        |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "regions",
        "zones",
        "instance_types"
      ],
      "description": "Which part of the catalog to list"
    },
    "csp": {
      "type": "string",
      "enum": [
        "kinesis",
        "aws",
        "azure"
      ],
      "description": "Cloud provider"
    },
    "region": {
      "description": "Provider region id; required for zones and instance types",
      "type": "string",
      "minLength": 1
    },
    "zone": {
      "type": "string",
      "minLength": 1,
      "description": "Limit to one availability zone"
    },
    "search": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "description": "Substring to search names and GPU types for"
    },
    "all": {
      "type": "boolean",
      "description": "List the full catalog instead of the popular picks"
    }
  },
  "required": [
    "kind",
    "csp"
  ],
  "additionalProperties": false
}
```

</details>

### Result

**`catalog.regions`**

* `regions` (`array<object>`): See the complete response schema below.
* `defaultRegion` (`string`): Region id chosen by default

<details>

<summary>Response schema: catalog.regions</summary>

```json
{
  "type": "object",
  "properties": {
    "regions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Region id to use when provisioning"
          },
          "name": {
            "type": "string"
          },
          "location": {
            "type": "string",
            "description": "City or area"
          },
          "recommended": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "name",
          "location",
          "recommended"
        ],
        "additionalProperties": false
      }
    },
    "defaultRegion": {
      "type": "string",
      "description": "Region id chosen by default"
    }
  },
  "required": [
    "regions",
    "defaultRegion"
  ],
  "additionalProperties": false
}
```

</details>

\*\*\`catalog.zones\`\*\*

* `zones` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: catalog.zones</summary>

```json
{
  "type": "object",
  "properties": {
    "zones": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "zones"
  ],
  "additionalProperties": false
}
```

</details>

\*\*\`catalog.instanceTypes\`\*\*

* `instanceTypes` (`array<object>`): Cheapest first

<details>

<summary>Response schema: catalog.instanceTypes</summary>

```json
{
  "type": "object",
  "properties": {
    "instanceTypes": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Instance type id to use when provisioning"
          },
          "name": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "description": "General Purpose, Compute Optimized, GPU, ..."
          },
          "specs": {
            "type": "object",
            "properties": {
              "vCpus": {
                "type": "number"
              },
              "memory": {
                "type": "string",
                "description": "Human-readable, e.g. 8 GiB"
              },
              "storage": {
                "type": "string"
              },
              "networkPerformance": {
                "type": "string"
              },
              "gpuType": {
                "type": "string"
              },
              "gpuManufacturer": {
                "type": "string"
              },
              "gpuCount": {
                "type": "number"
              },
              "gpuMemory": {
                "type": "string"
              }
            },
            "required": [
              "vCpus",
              "memory"
            ],
            "additionalProperties": false
          },
          "pricePerHour": {
            "type": "number",
            "description": "Customer price in USD per hour"
          },
          "inStock": {
            "description": "null when the provider does not report stock",
            "type": [
              "boolean",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "name",
          "category",
          "specs",
          "pricePerHour",
          "inStock"
        ],
        "additionalProperties": false
      },
      "description": "Cheapest first"
    }
  },
  "required": [
    "instanceTypes"
  ],
  "additionalProperties": false
}
```

</details>

## `browse_marketplace`

Browse spot capacity on the marketplace by hardware profile and country

**Group:** `catalog` · **Behavior:** Read

**Compact tool:** `catalog` · **Action:** `browse_marketplace`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation            | REST               | Required scopes |
| -------------------- | ------------------ | --------------- |
| `marketplace.browse` | `GET /marketplace` | `pools:read`    |

### Arguments

| Argument            | Type            | Required | Description                                  |
| ------------------- | --------------- | -------- | -------------------------------------------- |
| `gpuTypes`          | `array<string>` | No       | GPU models to include, e.g. H100             |
| `noGpu`             | `boolean`       | No       | CPU-only machines                            |
| `gpusPerMachine`    | `integer`       | No       | Exact GPU count per machine                  |
| `minCpus`           | `integer`       | No       | Minimum physical CPU packages                |
| `minRamGb`          | `integer`       | No       | Minimum RAM in GB                            |
| `minStorageGb`      | `integer`       | No       | Minimum disk in GB                           |
| `location`          | `string`        | No       | Marketplace location key                     |
| `vettedOnly`        | `boolean`       | No       | Only machines from vetted providers          |
| `tierIds`           | `array<string>` | No       | Provider tiers to include                    |
| `minGuaranteedDays` | `integer`       | No       | Minimum availability guarantee               |
| `minNoticeHours`    | `integer`       | No       | Minimum notice before a machine is withdrawn |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "gpuTypes": {
      "maxItems": 20,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "description": "GPU models to include, e.g. H100"
    },
    "noGpu": {
      "type": "boolean",
      "description": "CPU-only machines"
    },
    "gpusPerMachine": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "Exact GPU count per machine"
    },
    "minCpus": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "Minimum physical CPU packages"
    },
    "minRamGb": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "Minimum RAM in GB"
    },
    "minStorageGb": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "Minimum disk in GB"
    },
    "location": {
      "type": "string",
      "minLength": 1,
      "description": "Marketplace location key"
    },
    "vettedOnly": {
      "type": "boolean",
      "description": "Only machines from vetted providers"
    },
    "tierIds": {
      "maxItems": 20,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "description": "Provider tiers to include"
    },
    "minGuaranteedDays": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "Minimum availability guarantee"
    },
    "minNoticeHours": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "Minimum notice before a machine is withdrawn"
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `listings` (`array<object>`): See the complete response schema below.
* `summary` (`object`): See the complete response schema below.
* `truncated` (`boolean`): The scan hit its cap; counts are lower bounds

<details>

<summary>Response schema: marketplace.browse</summary>

```json
{
  "type": "object",
  "properties": {
    "listings": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "configId": {
            "type": "string",
            "description": "Stable id of this hardware profile and country bucket"
          },
          "hardwareProfile": {
            "type": "string"
          },
          "gpuModel": {
            "description": "null for CPU-only machines",
            "type": [
              "string",
              "null"
            ]
          },
          "gpusPerMachine": {
            "type": "integer"
          },
          "vramGbPerGpu": {
            "type": "number"
          },
          "cpuThreads": {
            "type": "number"
          },
          "minCpuPackages": {
            "description": "Smallest physical CPU count in the bucket",
            "type": "number"
          },
          "ramGb": {
            "type": "number"
          },
          "storageGb": {
            "type": "number"
          },
          "countryCode": {
            "type": "string",
            "description": "ISO country code; empty when unreported"
          },
          "locationLabel": {
            "type": "string"
          },
          "tierIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "isVetted": {
            "type": "boolean"
          },
          "totalMachines": {
            "type": "integer"
          },
          "availableMachines": {
            "type": "integer"
          },
          "minPriceCentsPerHour": {
            "type": "number",
            "description": "Cheapest machine-hour in the bucket"
          },
          "maxPriceCentsPerHour": {
            "type": "number"
          },
          "pricePerGpuHourCents": {
            "type": "number"
          },
          "guaranteedDays": {
            "description": "Weakest availability guarantee in the bucket",
            "type": "number"
          },
          "isOpenEnded": {
            "type": "boolean",
            "description": "Every machine is available until its provider changes its mind"
          },
          "noticeHours": {
            "type": "number"
          }
        },
        "required": [
          "configId",
          "hardwareProfile",
          "gpuModel",
          "gpusPerMachine",
          "countryCode",
          "locationLabel",
          "tierIds",
          "isVetted",
          "totalMachines",
          "availableMachines",
          "minPriceCentsPerHour",
          "maxPriceCentsPerHour",
          "isOpenEnded",
          "noticeHours"
        ],
        "additionalProperties": false
      }
    },
    "summary": {
      "type": "object",
      "properties": {
        "availableMachines": {
          "type": "integer"
        },
        "configurations": {
          "type": "integer"
        },
        "fromPriceCentsPerGpuHour": {
          "type": "number"
        },
        "fromPriceCentsPerHour": {
          "type": "number"
        }
      },
      "required": [
        "availableMachines",
        "configurations"
      ],
      "additionalProperties": false
    },
    "truncated": {
      "type": "boolean",
      "description": "The scan hit its cap; counts are lower bounds"
    }
  },
  "required": [
    "listings",
    "summary",
    "truncated"
  ],
  "additionalProperties": false
}
```

</details>


# Billing

Inspect the organization’s balance and costs with its admin permissions.

Inspect the organization’s balance and costs with its admin permissions.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

Billing is per organization and requires an admin role. Pass its slug as `org`. MCP does not add credits or configure auto-reload; on a balance refusal, explain the entitlement and leave billing to an organization admin. MCP money errors omit payment links. REST retains checkout sessions, saved-card top-ups and auto-reload updates.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `get_balance`

Current balance and billing status of the caller's organization

**Group:** `billing` · **Behavior:** Read

**Compact tool:** `billing` · **Action:** `get_balance`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation         | REST                   | Required scopes |
| ----------------- | ---------------------- | --------------- |
| `billing.balance` | `GET /billing/balance` | `billing:read`  |

### Arguments

| Argument | Type     | Required | Description                                                                                      |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `org`    | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `balanceCents` (`number`): Paid balance
* `promoBalanceCents` (`number`): Promotional credit, spent first
* `currency` (`string`): See the complete response schema below.
* `overLimitCents` (`number`): How far below zero the balance may go
* `status` (`string or null`): See the complete response schema below.
* `mode` (`string`): Billing mode of the organization
* `frozen` (`boolean`): The organization is frozen: writes are refused until it is unfrozen
* `autoReload` (`object`): See the complete response schema below.

<details>

<summary>Response schema: billing.balance</summary>

```json
{
  "type": "object",
  "properties": {
    "balanceCents": {
      "type": "number",
      "description": "Paid balance"
    },
    "promoBalanceCents": {
      "type": "number",
      "description": "Promotional credit, spent first"
    },
    "currency": {
      "type": "string"
    },
    "overLimitCents": {
      "type": "number",
      "description": "How far below zero the balance may go"
    },
    "status": {
      "type": [
        "string",
        "null"
      ]
    },
    "mode": {
      "type": "string",
      "description": "Billing mode of the organization"
    },
    "frozen": {
      "type": "boolean",
      "description": "The organization is frozen: writes are refused until it is unfrozen"
    },
    "autoReload": {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "The saved card is charged automatically when the balance drops below the threshold"
        },
        "thresholdCents": {
          "description": "Balance that triggers a reload",
          "type": [
            "number",
            "null"
          ]
        },
        "targetCents": {
          "description": "Balance after a reload",
          "type": [
            "number",
            "null"
          ]
        }
      },
      "required": [
        "enabled",
        "thresholdCents",
        "targetCents"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "balanceCents",
    "promoBalanceCents",
    "currency",
    "overLimitCents",
    "status",
    "mode",
    "frozen",
    "autoReload"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `get_costs`

Cost breakdown for a date range of up to 366 days. Default: the current calendar month, UTC

**Group:** `billing` · **Behavior:** Read

**Compact tool:** `billing` · **Action:** `get_costs`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation       | REST                 | Required scopes |
| --------------- | -------------------- | --------------- |
| `billing.costs` | `GET /billing/costs` | `billing:read`  |

### Arguments

| Argument    | Type     | Required | Description                                                                                      |
| ----------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `start`     | `string` | No       | First day, YYYY-MM-DD                                                                            |
| `end`       | `string` | No       | Last day, YYYY-MM-DD                                                                             |
| `projectId` | `string` | No       | Limit to one project                                                                             |
| `appId`     | `string` | No       | Limit to one app                                                                                 |
| `org`       | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "start": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "First day, YYYY-MM-DD"
    },
    "end": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Last day, YYYY-MM-DD"
    },
    "projectId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Limit to one project"
    },
    "appId": {
      "type": "string",
      "pattern": "^[0-9a-fA-F]{24}$",
      "description": "Limit to one app"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `start` (`string`): ISO 8601 timestamp (UTC)
* `end` (`string`): ISO 8601 timestamp (UTC)
* `currency` (`string`): See the complete response schema below.
* `totalCost` (`number`): Total for the range, in the billing currency
* `byProject` (`array<object>`): See the complete response schema below.
* `byApp` (`array<object>`): See the complete response schema below.
* `byResource` (`array<object>`): Cost per billed resource (CPU, GPU, RAM, storage, bandwidth, ...)
* `daily` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: billing.costs</summary>

```json
{
  "type": "object",
  "properties": {
    "start": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    },
    "end": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp (UTC)"
    },
    "currency": {
      "type": "string"
    },
    "totalCost": {
      "type": "number",
      "description": "Total for the range, in the billing currency"
    },
    "byProject": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "projectId": {
            "type": "string"
          },
          "projectName": {
            "type": "string"
          },
          "cost": {
            "type": "number"
          }
        },
        "required": [
          "projectId",
          "projectName",
          "cost"
        ],
        "additionalProperties": false
      }
    },
    "byApp": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "appId": {
            "type": "string"
          },
          "appName": {
            "type": "string"
          },
          "projectId": {
            "type": "string"
          },
          "cost": {
            "type": "number"
          }
        },
        "required": [
          "appId",
          "appName",
          "projectId",
          "cost"
        ],
        "additionalProperties": false
      }
    },
    "byResource": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          },
          "cost": {
            "type": "number"
          },
          "usage": {
            "type": "number"
          },
          "unit": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "cost",
          "usage",
          "unit"
        ],
        "additionalProperties": false
      },
      "description": "Cost per billed resource (CPU, GPU, RAM, storage, bandwidth, ...)"
    },
    "daily": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "description": "YYYY-MM-DD"
          },
          "cost": {
            "type": "number"
          }
        },
        "required": [
          "date",
          "cost"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "start",
    "end",
    "currency",
    "totalCost",
    "byProject",
    "byApp",
    "byResource",
    "daily"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`.


# Invoices and payment settings

Read invoices and ledger history, manage saved cards, redeem vouchers and configure node protection.

Read invoices and ledger history, manage saved cards, redeem vouchers and configure node protection.

These are action names. In default compact mode, call the indicated group tool with `{"action":"<action-name>","params":{...}}`; the arguments below go inside `params`. In `?mode=all`, call the action name directly with those arguments. `browse_github` and `read_docs` are direct tools in both modes. Expand each schema for nested fields, enum values and validation constraints. Successful results are JSON in both text content and `structuredContent`; failures use `isError` and `structuredContent.error`.

The [OpenAI profile](/mcp/openai-profile) calls individual actions, omits `get_byod_token`, and restricts credential inputs and environment outputs. The input schemas below describe the standard catalog; profile-specific schemas are included where they differ.

## `list_invoices`

List the organization's monthly invoices, newest first. Optionally for one month, status or date range

**Group:** `billing` · **Behavior:** Read

**Compact tool:** `billing` · **Action:** `list_invoices`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation               | REST                    | Required scopes |
| ----------------------- | ----------------------- | --------------- |
| `billing.invoices.list` | `GET /billing/invoices` | `billing:read`  |

### Arguments

| Argument | Type                             | Required | Description                                                                                      |
| -------- | -------------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `year`   | `integer`                        | No       | Invoices of one year                                                                             |
| `month`  | `integer`                        | No       | Invoices of one month (with year)                                                                |
| `status` | `"draft" / "finalized" / "paid"` | No       | Invoices in this state                                                                           |
| `from`   | `string`                         | No       | Billing periods starting on or after this day, YYYY-MM-DD                                        |
| `to`     | `string`                         | No       | Billing periods ending on or before this day, YYYY-MM-DD                                         |
| `org`    | `string`                         | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "year": {
      "type": "integer",
      "minimum": 2020,
      "maximum": 2100,
      "description": "Invoices of one year"
    },
    "month": {
      "type": "integer",
      "minimum": 1,
      "maximum": 12,
      "description": "Invoices of one month (with year)"
    },
    "status": {
      "type": "string",
      "enum": [
        "draft",
        "finalized",
        "paid"
      ],
      "description": "Invoices in this state"
    },
    "from": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Billing periods starting on or after this day, YYYY-MM-DD"
    },
    "to": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Billing periods ending on or before this day, YYYY-MM-DD"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `invoices` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: billing.invoices.list</summary>

```json
{
  "type": "object",
  "properties": {
    "invoices": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "Invoice number; what the other invoice operations take"
          },
          "period": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "description": "YYYY-MM-DD"
              },
              "end": {
                "type": "string",
                "description": "YYYY-MM-DD"
              }
            },
            "required": [
              "start",
              "end"
            ],
            "additionalProperties": false,
            "description": "Billing period, inclusive"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "finalized",
              "paid"
            ]
          },
          "partialMonth": {
            "type": "boolean",
            "description": "The organization's billing began during the period"
          },
          "currency": {
            "type": "string"
          },
          "totalCents": {
            "type": "integer",
            "description": "Usage charged in the period"
          },
          "amountDueCents": {
            "type": "integer",
            "description": "What the organization owes on this invoice; 0 when credit covered it"
          },
          "closingBalanceCents": {
            "type": "integer",
            "description": "Balance at the end of the period; negative means owed"
          },
          "createdOn": {
            "type": "string",
            "description": "YYYY-MM-DD"
          },
          "updatedOn": {
            "anyOf": [
              {
                "type": "string",
                "description": "YYYY-MM-DD"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "number",
          "period",
          "status",
          "partialMonth",
          "currency",
          "totalCents",
          "amountDueCents",
          "closingBalanceCents",
          "createdOn",
          "updatedOn"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "invoices"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `get_invoice`

Get one invoice with its summary, itemized usage, payments and credits. The PDF is only available through the REST API (GET /v1/billing/invoices/{invoiceNumber}/pdf).

**Group:** `billing` · **Behavior:** Read

**Compact tool:** `billing` · **Action:** `get_invoice`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation              | REST                                    | Required scopes |
| ---------------------- | --------------------------------------- | --------------- |
| `billing.invoices.get` | `GET /billing/invoices/{invoiceNumber}` | `billing:read`  |

### Arguments

| Argument        | Type     | Required | Description                                                                                      |
| --------------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `invoiceNumber` | `string` | Yes      | Invoice number                                                                                   |
| `org`           | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "invoiceNumber": {
      "type": "string",
      "minLength": 1,
      "description": "Invoice number"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "invoiceNumber"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `number` (`string`): Invoice number; what the other invoice operations take
* `period` (`object`): Billing period, inclusive
* `status` (`"draft" / "finalized" / "paid"`): See the complete response schema below.
* `partialMonth` (`boolean`): The organization's billing began during the period
* `currency` (`string`): See the complete response schema below.
* `totalCents` (`integer`): Usage charged in the period
* `amountDueCents` (`integer`): What the organization owes on this invoice; 0 when credit covered it
* `closingBalanceCents` (`integer`): Balance at the end of the period; negative means owed
* `createdOn` (`string`): YYYY-MM-DD
* `updatedOn` (`string or null`): See the complete response schema below.
* `customer` (`object`): See the complete response schema below.
* `statement` (`object`): The statement summary: how the balance moved over the period
* `items` (`array<object>`): Itemized usage
* `credits` (`array<object>`): Payments, promos, refunds and adjustments within the period
* `finalizedOn` (`string or null`): See the complete response schema below.
* `paidOn` (`string or null`): See the complete response schema below.

<details>

<summary>Response schema: billing.invoices.get</summary>

```json
{
  "type": "object",
  "properties": {
    "number": {
      "type": "string",
      "description": "Invoice number; what the other invoice operations take"
    },
    "period": {
      "type": "object",
      "properties": {
        "start": {
          "type": "string",
          "description": "YYYY-MM-DD"
        },
        "end": {
          "type": "string",
          "description": "YYYY-MM-DD"
        }
      },
      "required": [
        "start",
        "end"
      ],
      "additionalProperties": false,
      "description": "Billing period, inclusive"
    },
    "status": {
      "type": "string",
      "enum": [
        "draft",
        "finalized",
        "paid"
      ]
    },
    "partialMonth": {
      "type": "boolean",
      "description": "The organization's billing began during the period"
    },
    "currency": {
      "type": "string"
    },
    "totalCents": {
      "type": "integer",
      "description": "Usage charged in the period"
    },
    "amountDueCents": {
      "type": "integer",
      "description": "What the organization owes on this invoice; 0 when credit covered it"
    },
    "closingBalanceCents": {
      "type": "integer",
      "description": "Balance at the end of the period; negative means owed"
    },
    "createdOn": {
      "type": "string",
      "description": "YYYY-MM-DD"
    },
    "updatedOn": {
      "anyOf": [
        {
          "type": "string",
          "description": "YYYY-MM-DD"
        },
        {
          "type": "null"
        }
      ]
    },
    "customer": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string"
        },
        "name": {
          "type": [
            "string",
            "null"
          ]
        },
        "billingEmail": {
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "email",
        "name",
        "billingEmail"
      ],
      "additionalProperties": false
    },
    "statement": {
      "type": "object",
      "properties": {
        "openingBalanceCents": {
          "type": "integer"
        },
        "closingBalanceCents": {
          "type": "integer"
        },
        "amountDueCents": {
          "type": "integer"
        },
        "paymentsCents": {
          "type": "integer",
          "description": "Card and off-platform payments received in the period"
        },
        "promosCents": {
          "type": "integer",
          "description": "Promotional credit granted in the period"
        },
        "refundsCents": {
          "type": "integer",
          "description": "Signed"
        },
        "adjustmentsCents": {
          "type": "integer",
          "description": "Signed"
        },
        "usageCents": {
          "type": "integer",
          "description": "Usage charged in the period"
        },
        "taxCents": {
          "type": "integer",
          "description": "Tax due on the amount owed; 0 when nothing is owed or no rate applies"
        },
        "taxRateBps": {
          "type": "integer",
          "description": "Tax rate the invoice was billed under, in basis points"
        }
      },
      "required": [
        "openingBalanceCents",
        "closingBalanceCents",
        "amountDueCents",
        "paymentsCents",
        "promosCents",
        "refundsCents",
        "adjustmentsCents",
        "usageCents",
        "taxCents",
        "taxRateBps"
      ],
      "additionalProperties": false,
      "description": "The statement summary: how the balance moved over the period"
    },
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "resource": {
            "type": "string",
            "description": "What was billed, e.g. a GPU model, RAM, storage, a server"
          },
          "detail": {
            "type": [
              "string",
              "null"
            ]
          },
          "csp": {
            "type": [
              "string",
              "null"
            ]
          },
          "region": {
            "type": [
              "string",
              "null"
            ]
          },
          "zone": {
            "type": [
              "string",
              "null"
            ]
          },
          "kcRegion": {
            "type": [
              "string",
              "null"
            ]
          },
          "quantity": {
            "type": "number"
          },
          "unit": {
            "type": "string"
          },
          "unitPrice": {
            "type": "number",
            "description": "Price per unit, in currency units (fractional)"
          },
          "amount": {
            "type": "number",
            "description": "quantity times unitPrice, in currency units"
          }
        },
        "required": [
          "resource",
          "detail",
          "csp",
          "region",
          "zone",
          "kcRegion",
          "quantity",
          "unit",
          "unitPrice",
          "amount"
        ],
        "additionalProperties": false
      },
      "description": "Itemized usage"
    },
    "credits": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "ledgerEntryId": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "Ledger entry type: credit_topup, manual_payment, a promo, refund, adjustment"
          },
          "at": {
            "type": "string",
            "description": "ISO 8601 timestamp"
          },
          "amountCents": {
            "type": "integer",
            "description": "Signed"
          },
          "label": {
            "type": "string"
          },
          "paymentMethod": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "brand": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "last4": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "brand",
                  "last4"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "ledgerEntryId",
          "type",
          "at",
          "amountCents",
          "label",
          "paymentMethod"
        ],
        "additionalProperties": false
      },
      "description": "Payments, promos, refunds and adjustments within the period"
    },
    "finalizedOn": {
      "anyOf": [
        {
          "type": "string",
          "description": "YYYY-MM-DD"
        },
        {
          "type": "null"
        }
      ]
    },
    "paidOn": {
      "anyOf": [
        {
          "type": "string",
          "description": "YYYY-MM-DD"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "number",
    "period",
    "status",
    "partialMonth",
    "currency",
    "totalCents",
    "amountDueCents",
    "closingBalanceCents",
    "createdOn",
    "updatedOn",
    "customer",
    "statement",
    "items",
    "credits",
    "finalizedOn",
    "paidOn"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `list_charges`

Usage charges summed per day or per hour, newest first. From the ledger, with the split between serverless apps, dedicated servers and spot leases

**Group:** `billing` · **Behavior:** Read

**Compact tool:** `billing` · **Action:** `list_charges`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation              | REST                   | Required scopes |
| ---------------------- | ---------------------- | --------------- |
| `billing.charges.list` | `GET /billing/charges` | `billing:read`  |

### Arguments

| Argument      | Type             | Required | Description                                                                                      |
| ------------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `granularity` | `"day" / "hour"` | No       | Sum per day (default) or per hour                                                                |
| `from`        | `string`         | No       | First day to include, YYYY-MM-DD                                                                 |
| `to`          | `string`         | No       | Last day to include, YYYY-MM-DD                                                                  |
| `timeZone`    | `string`         | No       | IANA time zone the days are counted in, e.g. America/Los\_Angeles; UTC by default                |
| `limit`       | `integer`        | No       | Items per page, 1 to 200 (default 50)                                                            |
| `cursor`      | `string`         | No       | nextCursor from the previous page; omit for the first page                                       |
| `org`         | `string`         | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "granularity": {
      "type": "string",
      "enum": [
        "day",
        "hour"
      ],
      "description": "Sum per day (default) or per hour"
    },
    "from": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "First day to include, YYYY-MM-DD"
    },
    "to": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Last day to include, YYYY-MM-DD"
    },
    "timeZone": {
      "type": "string",
      "description": "IANA time zone the days are counted in, e.g. America/Los_Angeles; UTC by default"
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 200,
      "description": "Items per page, 1 to 200 (default 50)"
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "nextCursor from the previous page; omit for the first page"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `charges` (`array<object>`): See the complete response schema below.
* `nextCursor` (`string or null`): Pass as cursor to fetch the next page; null on the last page

<details>

<summary>Response schema: billing.charges.list</summary>

```json
{
  "type": "object",
  "properties": {
    "charges": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "period": {
            "type": "string",
            "format": "date-time",
            "description": "Start of the day or hour, in the requested time zone"
          },
          "amountCents": {
            "type": "number",
            "description": "Usage charged in the period, including fractional cents"
          },
          "appsCents": {
            "type": "number",
            "description": "Of amountCents, serverless app usage"
          },
          "serversCents": {
            "type": "number",
            "description": "Of amountCents, dedicated servers"
          },
          "spotCents": {
            "type": "number",
            "description": "Of amountCents, spot leases"
          },
          "currency": {
            "type": "string"
          }
        },
        "required": [
          "period",
          "amountCents",
          "appsCents",
          "serversCents",
          "spotCents",
          "currency"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": {
      "description": "Pass as cursor to fetch the next page; null on the last page",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "charges",
    "nextCursor"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `list_credits`

Ledger entries that added or removed credit, newest first. Payments, promotional credit, refunds, adjustments

**Group:** `billing` · **Behavior:** Read

**Compact tool:** `billing` · **Action:** `list_credits`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation              | REST                   | Required scopes |
| ---------------------- | ---------------------- | --------------- |
| `billing.credits.list` | `GET /billing/credits` | `billing:read`  |

### Arguments

| Argument | Type                                                                                                                   | Required | Description                                                                                      |
| -------- | ---------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `type`   | `"credit_topup" / "manual_payment" / "signup_promo" / "credit_promo" / "social_media_promo" / "refund" / "adjustment"` | No       | Only entries of this type                                                                        |
| `from`   | `string`                                                                                                               | No       | First day to include, YYYY-MM-DD (UTC)                                                           |
| `to`     | `string`                                                                                                               | No       | Last day to include, YYYY-MM-DD (UTC)                                                            |
| `limit`  | `integer`                                                                                                              | No       | Items per page, 1 to 200 (default 50)                                                            |
| `cursor` | `string`                                                                                                               | No       | nextCursor from the previous page; omit for the first page                                       |
| `org`    | `string`                                                                                                               | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "type": {
      "type": "string",
      "enum": [
        "credit_topup",
        "manual_payment",
        "signup_promo",
        "credit_promo",
        "social_media_promo",
        "refund",
        "adjustment"
      ],
      "description": "Only entries of this type"
    },
    "from": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "First day to include, YYYY-MM-DD (UTC)"
    },
    "to": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Last day to include, YYYY-MM-DD (UTC)"
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 200,
      "description": "Items per page, 1 to 200 (default 50)"
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "maxLength": 512,
      "description": "nextCursor from the previous page; omit for the first page"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `entries` (`array<object>`): See the complete response schema below.
* `nextCursor` (`string or null`): Pass as cursor to fetch the next page; null on the last page

<details>

<summary>Response schema: billing.credits.list</summary>

```json
{
  "type": "object",
  "properties": {
    "entries": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Ledger entry id"
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          },
          "type": {
            "type": "string",
            "enum": [
              "credit_topup",
              "manual_payment",
              "signup_promo",
              "credit_promo",
              "social_media_promo",
              "refund",
              "adjustment"
            ],
            "description": "credit_topup and manual_payment are money in; the promos are granted credit"
          },
          "amountCents": {
            "type": "integer",
            "description": "Signed: positive adds credit, negative removes it"
          },
          "currency": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "succeeded, pending or failed"
          },
          "paymentMethod": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "brand": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "last4": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                },
                "required": [
                  "brand",
                  "last4"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "The card charged, for card payments"
          },
          "note": {
            "description": "A note left by the person who recorded a manual entry",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "at",
          "type",
          "amountCents",
          "currency",
          "status",
          "paymentMethod",
          "note"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": {
      "description": "Pass as cursor to fetch the next page; null on the last page",
      "type": [
        "string",
        "null"
      ]
    }
  },
  "required": [
    "entries",
    "nextCursor"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `list_payment_methods`

List the organization's saved cards and which one is the default

**Group:** `billing` · **Behavior:** Read

**Compact tool:** `billing` · **Action:** `list_payment_methods`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                     | REST                           | Required scopes |
| ----------------------------- | ------------------------------ | --------------- |
| `billing.paymentMethods.list` | `GET /billing/payment-methods` | `billing:read`  |

### Arguments

| Argument | Type     | Required | Description                                                                                      |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `org`    | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `paymentMethods` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: billing.paymentMethods.list</summary>

```json
{
  "type": "object",
  "properties": {
    "paymentMethods": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Payment method id"
          },
          "brand": {
            "description": "Card brand, e.g. visa",
            "type": [
              "string",
              "null"
            ]
          },
          "last4": {
            "type": [
              "string",
              "null"
            ]
          },
          "expMonth": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "expYear": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "default": {
            "type": "boolean",
            "description": "The card auto-reload and top-ups charge"
          },
          "addedAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          }
        },
        "required": [
          "id",
          "brand",
          "last4",
          "expMonth",
          "expYear",
          "default",
          "addedAt"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "paymentMethods"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `set_default_payment_method`

Make a saved card the default for auto-reload and top-ups

**Group:** `billing` · **Behavior:** Write

**Compact tool:** `billing_write` · **Action:** `set_default_payment_method`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                           | REST                                                      | Required scopes |
| ----------------------------------- | --------------------------------------------------------- | --------------- |
| `billing.paymentMethods.setDefault` | `POST /billing/payment-methods/{paymentMethodId}/default` | `billing:write` |

### Arguments

| Argument          | Type     | Required | Description                                                                                      |
| ----------------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `paymentMethodId` | `string` | Yes      | Payment method id from the list                                                                  |
| `org`             | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "paymentMethodId": {
      "type": "string",
      "minLength": 1,
      "description": "Payment method id from the list"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "paymentMethodId"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `paymentMethods` (`array<object>`): See the complete response schema below.

<details>

<summary>Response schema: billing.paymentMethods.setDefault</summary>

```json
{
  "type": "object",
  "properties": {
    "paymentMethods": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Payment method id"
          },
          "brand": {
            "description": "Card brand, e.g. visa",
            "type": [
              "string",
              "null"
            ]
          },
          "last4": {
            "type": [
              "string",
              "null"
            ]
          },
          "expMonth": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "expYear": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "default": {
            "type": "boolean",
            "description": "The card auto-reload and top-ups charge"
          },
          "addedAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          }
        },
        "required": [
          "id",
          "brand",
          "last4",
          "expMonth",
          "expYear",
          "default",
          "addedAt"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "paymentMethods"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `404 not_found`, `400 org_required`, `403 key_project_restricted`.

## `remove_payment_method`

Remove a saved card. A removed default is replaced by the next saved card; auto-reload is switched off when no card is left.

**Group:** `billing` · **Behavior:** Write

**Compact tool:** `billing_write` · **Action:** `remove_payment_method`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

Requires `confirm: true` for this irreversible action.

### Permissions and REST equivalents

| Operation                       | REST                                                | Required scopes |
| ------------------------------- | --------------------------------------------------- | --------------- |
| `billing.paymentMethods.remove` | `DELETE /billing/payment-methods/{paymentMethodId}` | `billing:write` |

### Arguments

| Argument          | Type     | Required | Description                                                                                      |
| ----------------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `paymentMethodId` | `string` | Yes      | Payment method id from the list                                                                  |
| `confirm`         | `true`   | Yes      | Must be true: this action cannot be undone                                                       |
| `org`             | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "paymentMethodId": {
      "type": "string",
      "minLength": 1,
      "description": "Payment method id from the list"
    },
    "confirm": {
      "type": "boolean",
      "const": true,
      "description": "Must be true: this action cannot be undone"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "paymentMethodId",
    "confirm"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `paymentMethods` (`array<object>`): The cards that remain
* `autoReloadDisabled` (`boolean`): Auto-reload was switched off because no card is left

<details>

<summary>Response schema: billing.paymentMethods.remove</summary>

```json
{
  "type": "object",
  "properties": {
    "paymentMethods": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Payment method id"
          },
          "brand": {
            "description": "Card brand, e.g. visa",
            "type": [
              "string",
              "null"
            ]
          },
          "last4": {
            "type": [
              "string",
              "null"
            ]
          },
          "expMonth": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "expYear": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "default": {
            "type": "boolean",
            "description": "The card auto-reload and top-ups charge"
          },
          "addedAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 timestamp (UTC)"
          }
        },
        "required": [
          "id",
          "brand",
          "last4",
          "expMonth",
          "expYear",
          "default",
          "addedAt"
        ],
        "additionalProperties": false
      },
      "description": "The cards that remain"
    },
    "autoReloadDisabled": {
      "type": "boolean",
      "description": "Auto-reload was switched off because no card is left"
    }
  },
  "required": [
    "paymentMethods",
    "autoReloadDisabled"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `404 not_found`, `400 org_required`, `403 key_project_restricted`.

## `redeem_voucher`

Redeem a voucher code for organization credit; once per person per code

**Group:** `billing` · **Behavior:** Write

**Compact tool:** `billing_write` · **Action:** `redeem_voucher`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                 | REST                            | Required scopes |
| ------------------------- | ------------------------------- | --------------- |
| `billing.vouchers.redeem` | `POST /billing/vouchers/redeem` | `billing:write` |

### Arguments

| Argument | Type     | Required | Description                                                                                      |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `code`   | `string` | Yes      | The voucher code                                                                                 |
| `org`    | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "code": {
      "type": "string",
      "minLength": 1,
      "maxLength": 64,
      "description": "The voucher code"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "code"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `amountCents` (`integer`): Credit added to the balance
* `redemptionsRemaining` (`integer or null`): Uses the voucher has left

<details>

<summary>Response schema: billing.vouchers.redeem</summary>

```json
{
  "type": "object",
  "properties": {
    "amountCents": {
      "type": "integer",
      "description": "Credit added to the balance"
    },
    "redemptionsRemaining": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "description": "Uses the voucher has left"
    }
  },
  "required": [
    "amountCents",
    "redemptionsRemaining"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `404 voucher_not_found`, `400 voucher_rejected`, `429 too_many_attempts`, `400 org_required`, `403 key_project_restricted`.

## `get_node_protection`

Read the organization's node protection setting. Whether its dedicated servers are hibernated automatically when the balance is about to run out, and at how many hours of runway

**Group:** `billing` · **Behavior:** Read

**Compact tool:** `billing` · **Action:** `get_node_protection`

**MCP annotations:** `readOnlyHint: true` · `destructiveHint: false` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                    | REST                           | Required scopes |
| ---------------------------- | ------------------------------ | --------------- |
| `billing.nodeProtection.get` | `GET /billing/node-protection` | `billing:read`  |

### Arguments

| Argument | Type     | Required | Description                                                                                      |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------------------ |
| `org`    | `string` | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "additionalProperties": false
}
```

</details>

### Result

* `enabled` (`boolean`): The organization's dedicated servers are hibernated when the balance runs low
* `thresholdHours` (`integer or null`): Hours of runway left (balance over spend rate) at which the servers are hibernated; null when off
* `hibernatedAt` (`string or null`): When protection last hibernated the servers
* `deprovisionedAt` (`string or null`): When a balance past its limit last deprovisioned them

<details>

<summary>Response schema: billing.nodeProtection.get</summary>

```json
{
  "type": "object",
  "properties": {
    "enabled": {
      "type": "boolean",
      "description": "The organization's dedicated servers are hibernated when the balance runs low"
    },
    "thresholdHours": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "description": "Hours of runway left (balance over spend rate) at which the servers are hibernated; null when off"
    },
    "hibernatedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ],
      "description": "When protection last hibernated the servers"
    },
    "deprovisionedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ],
      "description": "When a balance past its limit last deprovisioned them"
    }
  },
  "required": [
    "enabled",
    "thresholdHours",
    "hibernatedAt",
    "deprovisionedAt"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.

## `set_node_protection`

Turn node protection on with a runway threshold, or off. On: when the balance would run out within thresholdHours at the current spend rate, the organization's dedicated servers are hibernated (storage only) until credits are added.

**Group:** `billing` · **Behavior:** Write

**Compact tool:** `billing_write` · **Action:** `set_node_protection`

**MCP annotations:** `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: true` · `openWorldHint: false`

### Permissions and REST equivalents

| Operation                       | REST                             | Required scopes |
| ------------------------------- | -------------------------------- | --------------- |
| `billing.nodeProtection.update` | `PATCH /billing/node-protection` | `billing:write` |

### Arguments

| Argument         | Type      | Required | Description                                                                                      |
| ---------------- | --------- | -------- | ------------------------------------------------------------------------------------------------ |
| `enabled`        | `boolean` | Yes      | Turn protection on or off                                                                        |
| `thresholdHours` | `integer` | No       | Hours of runway that trigger hibernation, 2 to 48; required when enabling                        |
| `org`            | `string`  | No       | Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one |

<details>

<summary>Complete input schema</summary>

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "enabled": {
      "type": "boolean",
      "description": "Turn protection on or off"
    },
    "thresholdHours": {
      "type": "integer",
      "minimum": 2,
      "maximum": 48,
      "description": "Hours of runway that trigger hibernation, 2 to 48; required when enabling"
    },
    "org": {
      "description": "Slug of the organization to act in (see orgs.list); needed when the caller reaches more than one",
      "type": "string",
      "minLength": 3,
      "maxLength": 39
    }
  },
  "required": [
    "enabled"
  ],
  "additionalProperties": false
}
```

</details>

### Result

* `enabled` (`boolean`): The organization's dedicated servers are hibernated when the balance runs low
* `thresholdHours` (`integer or null`): Hours of runway left (balance over spend rate) at which the servers are hibernated; null when off
* `hibernatedAt` (`string or null`): When protection last hibernated the servers
* `deprovisionedAt` (`string or null`): When a balance past its limit last deprovisioned them

<details>

<summary>Response schema: billing.nodeProtection.update</summary>

```json
{
  "type": "object",
  "properties": {
    "enabled": {
      "type": "boolean",
      "description": "The organization's dedicated servers are hibernated when the balance runs low"
    },
    "thresholdHours": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "description": "Hours of runway left (balance over spend rate) at which the servers are hibernated; null when off"
    },
    "hibernatedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ],
      "description": "When protection last hibernated the servers"
    },
    "deprovisionedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "description": "ISO 8601 timestamp (UTC)"
        },
        {
          "type": "null"
        }
      ],
      "description": "When a balance past its limit last deprovisioned them"
    }
  },
  "required": [
    "enabled",
    "thresholdHours",
    "hibernatedAt",
    "deprovisionedAt"
  ],
  "additionalProperties": false
}
```

</details>

Operation-specific errors: `400 org_required`, `403 key_project_restricted`.


# Plan your compute

Match workload requirements, operational control and cost to the right capacity.

A useful compute decision starts with the workload: what it requires, how long it runs, and how much control you need over its placement.

| Consideration | What to establish                                                                                 |
| ------------- | ------------------------------------------------------------------------------------------------- |
| Hardware      | CPU, RAM, disk, GPU model and VRAM requirements                                                   |
| Runtime       | Startup time, model downloads, sustained load and idle periods                                    |
| Location      | Region preferences and dependencies close to the workload                                         |
| Capacity      | Instance bounds, available servers and shared-grid quotas                                         |
| Operations    | Which organization owns and pays for it; Developers operate apps, admins manage grids and servers |
| Cost          | Serverless usage, dedicated-server charges and external infrastructure costs                      |

## Choose a starting point

**Serverless CPU** is a direct path when you want managed placement for a CPU workload. GPUs currently require a grid, using dedicated, marketplace or connected capacity.

**Dedicated cloud servers** give you explicit server capacity to organize into grids. Their lifecycle is separate from the apps using them.

**Bring your own compute** lets you use existing infrastructure. You remain responsible for the infrastructure's availability and its external costs.

## Measure before expanding

Begin with realistic resource requirements. Inspect logs and usage once the app is running, then adjust the configuration using measured behavior. More instances or different hardware can change both capacity requirements and spend.

For shared grids, check quotas and allocation policy before increasing the app's requirements. A resource being visible does not necessarily mean you can schedule additional workloads onto it.

## Plan the shutdown too

Stop applications that should no longer run. Separately review dedicated machines and outstanding server orders, and release capacity you no longer need. An organization admin can check Cost to understand remaining charges.

[Compare compute options](/getting-started/running-llms-with-kinesis-network/understanding-compute-sources) · [Understand grids](/getting-started/running-llms-with-kinesis-network/understanding-grids) · [Get guidance](/support)


# How Kinesis works

From an app definition to a running workload, with control at each step.

Kinesis separates the app you want to run from the compute that runs it. You describe the container, runtime and resources; the platform coordinates deployment onto compatible capacity.

```mermaid
flowchart LR
  A[Container image or GitHub repo] --> B[App configuration]
  B --> C{Compute target}
  C --> D[Serverless CPU capacity]
  C --> E[Dedicated or shared grid]
  D --> F[Running app instances]
  E --> F
  F --> G[Endpoints, logs and usage]
```

## 1. Define the workload

Choose an organization, then create an app inside a project you can edit. The organization owns the app and pays for its usage. Start with a container image or connect a GitHub repository for an image build. Configure environment variables, network ports, execution settings and resource requirements.

## 2. Choose where it runs

Use serverless CPU capacity for managed placement, or target a grid backed by dedicated cloud servers or infrastructure you bring. Available capacity, hardware requirements, placement constraints and grid quotas determine whether an app can be scheduled.

GPU apps require a grid. Serverless GPUs are currently paused.

## 3. Follow deployment

Creation, builds, provisioning and lifecycle changes can be asynchronous. An API response or successful portal action acknowledges the request. App status, build status and server orders tell you how execution is progressing.

## 4. Connect and operate

Use the configured endpoint for traffic, logs for application output, and metrics for resource usage. Configure the application's own authentication and network behavior to match its purpose. When an app fails to start, check its configuration and startup logs before retrying.

## 5. Review capacity and cost

Organization admins manage billing and infrastructure. Developers operate accessible apps; per-person grid quotas are charged against the app’s creator.

Stopping an app and releasing a dedicated server are separate actions. Check which resources remain allocated and their charging model before treating a workload as fully shut down.

The portal, [REST API](/api) and [MCP server](/mcp) provide different interfaces to these same resources.

[Deploy your first app](/getting-started/running-llms-with-kinesis-network) · [Choose compute](/getting-started/running-llms-with-kinesis-network/understanding-compute-sources)


# Platform principles

The principles behind the platform and the decisions you make with it.

Kinesis brings different sources of compute into a common deployment and operations workflow. Six principles guide that work.

## Reliability you can inspect

An accepted deployment request is the start of a process. Follow app status, readiness, logs and usage to understand what is happening, and use those signals to decide when a service is ready for traffic.

## Deliberate access

Choose where a workload runs and who can operate it. Resource sharing, API scopes and project restrictions express different parts of that decision. Select infrastructure appropriate to your data and requirements; discuss specific compliance needs with the team before deployment.

## Performance with context

Match CPU, RAM, GPU and storage to the workload. Then measure. Hardware requirements, capacity availability and placement choices all affect the result.

## Transparent operations

Configuration, status, logs and costs should help you explain the state of a deployment. The portal, REST API and MCP expose these resources through a shared platform model.

## Costs you can follow

Understand the charging model for the compute you choose. Serverless usage, dedicated servers and externally managed infrastructure have different cost implications. Review cost data alongside utilization, and release dedicated capacity when you no longer need it.

## A direct path from intent to deployment

Start with a template, container image or GitHub repository. Use the portal for interactive work, REST for integrations, and MCP for agent workflows.

[Explore the platform](/what-you-can-do-with-kinesis) · [Choose compute](/getting-started/running-llms-with-kinesis-network/understanding-compute-sources) · [Talk to the team](/support)


# Support

Get help with deployments, integrations and compute operations.

Email <support@kinesis.network> for account, deployment and infrastructure questions. For community discussion, join the [Kinesis Discord](https://discord.gg/ur9g7EctPt).

## Help us reproduce the issue

Include the operation you attempted, the time and time zone, the app or project ID, and the behavior you expected. For API or MCP failures, add the error code and request ID if one was returned.

A short, redacted log excerpt or minimal request example helps. Remove API keys, access tokens, passwords, payment information and environment variable secrets before sharing it.

## Useful starting points

| Issue                                             | Guide                                                                                                                                                                 |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| App will not start, or an endpoint is unavailable | [Monitor and troubleshoot](/getting-started/running-llms-with-kinesis-network/monitoring-apps)                                                                        |
| API authentication or missing permissions         | [Authentication and scopes](/api/authentication)                                                                                                                      |
| API error or failed asynchronous operation        | [Errors and recovery](/api/errors)                                                                                                                                    |
| MCP client cannot connect or cannot see a tool    | [Connect an MCP client](/mcp/connect)                                                                                                                                 |
| Choosing or contributing infrastructure           | [Compute options](/getting-started/running-llms-with-kinesis-network/understanding-compute-sources) · [Provider guide](/getting-started/kinetization-of-idle-compute) |


# PyTorch with WireGuard

How Kinesis supports PyTorch DDP?

By Toshihito Kikuchi

[PyTorch DDP](https://docs.pytorch.org/tutorials/intermediate/ddp_tutorial.html) (= Distributed Data Parallel) is becoming the industry standard to parallelize your model training process across multiple GPUs and machines. Since one of our missions is to provide scalable GPU computes, it’s essential to support PyTorch DDP on Kinesis Network.

Does Kinesis Network support PyTorch DDP? Yes, but it isn't quite "plug-and-play" yet — it requires a bit of manual tuning to get everything running smoothly. We will fully integrate it soon, but for now, you need some special configuration to run it. In this article, I’d like to explain why it’s a little bit tricky and what we're implementing behind the scene.

### Challenge in PyTorch networking

Okay, I know you have a model to train. You wrote a python script with [DistributedDataParallel](https://docs.pytorch.org/docs/stable/generated/torch.nn.parallel.DistributedDataParallel.html), something like this.

```python
import os
import torch
import torch.nn as nn
import torch.optim as optim
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP

def setup():
    # Initialize the process group
    # NCCL is the standard backend for NVIDIA GPUs
    dist.init_process_group(backend="nccl")
    torch.cuda.set_device(int(os.environ["LOCAL_RANK"]))

def cleanup():
    dist.destroy_process_group()

def run_training():
    setup()

    local_rank = int(os.environ["LOCAL_RANK"])
    steps = int(os.environ["STEPS"])
    rank = int(os.environ["RANK"])
    device = torch.device(f"cuda:{local_rank}")

    # 1. Define a tiny model
    model = nn.Linear(10, 10).to(device)
    model = DDP(model, device_ids=[local_rank])

    # 2. Setup Loss and Optimizer
    loss_fn = nn.MSELoss()
    optimizer = optim.SGD(model.parameters(), lr=0.001)

    # 3. Simple training loop (Synthetic data)
    print(f"[Rank {rank}] Starting training...")

    for step in range(steps):
        # Create random data on the fly
        inputs = torch.randn(20, 10).to(device)
        labels = torch.randn(20, 10).to(device)

        optimizer.zero_grad()
        outputs = model(inputs)
        loss = loss_fn(outputs, labels)
        loss.backward()
        optimizer.step()

        if step % 10 == 0 and rank == 0:
            print(f"Step {step} | Loss: {loss.item():.4f}")

    print(f"[Rank {rank}] Training complete.")
    cleanup()

if __name__ == "__main__":
    run_training()
```

We usually use `torchrun` to kick a distributed training process.  Below is the output to run a single-node, single-gpu training inside a container with detailed logs.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun \
  --nnodes=1 --nproc_per_node=1 --node_rank=0 \
  --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=127.0.0.1:29500 \
  train.py
I0406 17:13:28.244000 1265 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 127.0.0.1:29500
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_z0qxtusm
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 17:13:28.340000 1265 torch/distributed/launcher/api.py:224]
I0406 17:13:28.344000 1265 torch/distributed/elastic/agent/server/api.py:898] [default] starting workers for entrypoint: python3
I0406 17:13:28.345000 1265 torch/distributed/elastic/agent/server/api.py:693] [default] Rendezvous'ing worker group
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539] [default] Rendezvous complete for workers. Result:
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   restart_count=0
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   master_addr=2f3d44d8b0ea
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   master_port=43093
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   group_rank=0
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   group_world_size=1
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   local_ranks=[0]
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   role_ranks=[0]
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   global_ranks=[0]
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   role_world_sizes=[1]
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   global_world_sizes=[1]
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]   event_log_handler=null
I0406 17:13:28.481000 1265 torch/distributed/elastic/agent/server/api.py:539]
I0406 17:13:28.482000 1265 torch/distributed/elastic/agent/server/api.py:701] [default] Starting worker group
I0406 17:13:28.482000 1265 torch/distributed/elastic/agent/server/local_elastic_agent.py:299] use_agent_store: True
I0406 17:13:28.482000 1265 torch/distributed/elastic/agent/server/local_elastic_agent.py:195] Environment variable 'TORCHELASTIC_ENABLE_FILE_TIMER' not found. Do not start FileTimerServer.
I0406 17:13:28.482000 1265 torch/distributed/elastic/agent/server/local_elastic_agent.py:239] Environment variable 'TORCHELASTIC_HEALTH_CHECK_PORT' not found. Do not start health check.
2f3d44d8b0ea:1297:1297 [0] NCCL INFO ENV/Plugin: Could not find: libnccl-env.so
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Bootstrap: Using eth0:172.17.0.2<0>
2f3d44d8b0ea:1297:1297 [0] NCCL INFO cudaDriverVersion 12080
2f3d44d8b0ea:1297:1297 [0] NCCL INFO NCCL version 2.28.9+cuda12.9
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Comm config Blocking set to 1
2f3d44d8b0ea:1297:1297 [0] NCCL INFO NET/Plugin: Could not find: libnccl-net.so
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Failed to open libibverbs.so[.1]
2f3d44d8b0ea:1297:1297 [0] NCCL INFO transport/net_ib.cc:852 -> 3
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Failed to initialize NET plugin IB
2f3d44d8b0ea:1297:1297 [0] NCCL INFO NET/Socket : Using [0]eth0:172.17.0.2<0> [1]wg0:10.100.10.1<0>
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Initialized NET plugin Socket
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Assigned NET plugin Socket to comm
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Using network Socket
2f3d44d8b0ea:1297:1297 [0] NCCL INFO ncclCommInitRankConfig comm 0x3cd2a790 rank 0 nranks 1 cudaDev 0 nvmlDev 0 busId 70 commId 0xbda3ad566558fee7 - Init START
2f3d44d8b0ea:1297:1297 [0] NCCL INFO RAS client listening socket at ::1<28028>
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Bootstrap timings total 0.001224 (create 0.000070, send 0.000246, recv 0.000273, ring 0.000001, delay 0.000000)
2f3d44d8b0ea:1297:1297 [0] NCCL INFO NCCL_IGNORE_DISABLED_P2P set by environment to 1.
2f3d44d8b0ea:1297:1297 [0] NCCL INFO ncclTopoGetCpuAffinity: Affinity for GPU 0 is empty, ignoring. (GPU affinity =  ; CPU affinity = 0-27).
2f3d44d8b0ea:1297:1297 [0] NCCL INFO comm 0x3cd2a790 rank 0 nRanks 1 nNodes 1 localRanks 1 localRank 0 MNNVL 0
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Channel 00/64 : 0
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Channel 01/64 : 0
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Channel 02/64 : 0
...snip...
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Channel 63/64 : 0
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Trees [0] -1/-1/-1->0->-1 [1] -1/-1/-1->0->-1 [2] -1/-1/-1->0->-1 [3] -1/-1/-1->0->-1 [4] -1/-1/-1->0->-1 [5] -1/-1/-1->0->-1 [6] -1/-1/-1->0->-1 [7] -1/-1/-1->0->-1 [8] -1/-1/-1->0->-1 [9] -1/-1/-1->0->-1 [10] -1/-1/-1->0->-1 [11] -1/-1/-1->0->-1 [12] -1/-1/-1->0->-1 [13] -1/-1/-1->0->-1 [14] -1/-1/-1->0->-1 [15] -1/-1/-1->0->-1 [16] -1/-1/-1->0->-1 [17] -1/-1/-1->0->-1 [18] -1/-1/-1->0->-1 [19] -1/-1/-1->0->-1 [20] -1/-1/-1->0->-1 [21] -1/-1/-1->0->-1 [22] -1/-1/-1->0->-1 [23] -1/-1/-1->0->-1 [24] -1/-1/-1->0->-1 [25] -1/-1/-1->0->-1 [26] -1/-1/-1->0->-1 [27] -1/-1/-1->0->-1 [28] -1/-1/-1->0->-1 [29] -1/-1/-1->0->-1 [30] -1/-1/-1->0->-1 [31] -1/-1/-1->0->-1 [32] -1/-1/-1->0->-1 [33] -1/-1/-1->0->-1 [34] -1/-1/-1->0->-1 [35] -1/-1/-1->0->-1 [36] -1/-1/-1->0->-1 [37] -1/-1/-1->0->-1 [38] -1/-1/-1->0->-1 [39] -1/-1/-1->0->-1 [40] -1/-1/-1->0->-1 [41] -1/-1/-1->0->-1 [42] -1/-1/-1->0->-1 [43] -1/-1/-1->0->-1 [44] -1/-1/-1->0->-1 [45] -1/-1/-1->0->-1 [46] -1/-1/-1->0->-1 [47
2f3d44d8b0ea:1297:1297 [0] NCCL INFO P2P Chunksize set to 524288
2f3d44d8b0ea:1297:1297 [0] NCCL INFO PROFILER/Plugin: Could not find: libnccl-profiler.so
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Check P2P Type isAllDirectP2p 1 directMode 0 isAllCudaP2p 1
2f3d44d8b0ea:1297:1331 [0] NCCL INFO [Proxy Service] Device 0 CPU core 10
2f3d44d8b0ea:1297:1332 [0] NCCL INFO [Proxy Service UDS] Device 0 CPU core 9
2f3d44d8b0ea:1297:1297 [0] NCCL INFO TUNER/Plugin: Could not find: libnccl-tuner.so
2f3d44d8b0ea:1297:1297 [0] NCCL INFO 64 coll channels, 64 collnet channels, 0 nvls channels, 64 p2p channels, 64 p2p channels per peer
2f3d44d8b0ea:1297:1297 [0] NCCL INFO CC Off, workFifoBytes 1048576
2f3d44d8b0ea:1297:1297 [0] NCCL INFO ncclCommInitRankConfig comm 0x3cd2a790 rank 0 nranks 1 cudaDev 0 nvmlDev 0 busId 70 commId 0xbda3ad566558fee7 - Init COMPLETE
2f3d44d8b0ea:1297:1297 [0] NCCL INFO Init timings - ncclCommInitRankConfig: rank 0 nranks 1 total 0.17 (kernels 0.13, alloc 0.00, bootstrap 0.00, allgathers 0.00, topo 0.00, graphs 0.00, connections 0.02, rest 0.00)
[Rank 0] Starting training...
Step 0 | Loss: 1.1732
Step 10 | Loss: 1.1904
Step 20 | Loss: 1.5663
[Rank 0] Training complete.
2f3d44d8b0ea:1297:1297 [0] NCCL INFO comm 0x3cd2a790 rank 0 nranks 1 cudaDev 0 busId 70 - Destroy COMPLETE
2f3d44d8b0ea:1297:1297 [0] NCCL INFO ENV/Plugin: Closing env plugin ncclEnvDefault
I0406 17:13:32.492000 1265 torch/distributed/elastic/agent/server/api.py:917] [default] worker group successfully finished. Waiting 300 seconds for other agents to finish.
I0406 17:13:32.493000 1265 torch/distributed/elastic/agent/server/api.py:970] Local worker group finished (WorkerState.SUCCEEDED). Waiting 300 seconds for other agents to finish
I0406 17:13:32.493000 1265 torch/distributed/elastic/agent/server/api.py:984] Done waiting for other agents. Elapsed: 0.0004515647888183594 seconds
root@2f3d44d8b0ea:/app#
```

It looks pretty straightforward. Can we just specify `--nnodes` and `--node_rank` accordingly to run this on multiple gpu nodes? Well, unfortunately no, that’s why I’m writing this article.

The challenge is networking. In the example above, I specified `--rdzv_endpoint=127.0.0.1:29500`. Does this mean each node communicates with this endpoint during a training process, like the Hub-and-Spoke topology?  The answer is no.  As the name implies, it’s a rendezvous point.  Every node meets there, to retrieve the actual endpoint to communicate with. In the output above, you see `master_addr=2f3d44d8b0ea` and `master_port=43093` , that’s the actual endpoint each node communicates with. The master port, 43093 in this case, is dynamically assigned, which means we cannot predict which port needs to be opened. This is a problem in a Docker environment because we need to expose ports on creation but we don't know it on creation.  Should we use the host network `--network=host` or publish a wide range of ports like `-p 1000:65535`?  Apparently that design is far from ideal. We want our containers to be contained as much as possible.

In [the earlier article](https://docs.kinesis.network/blog/reaching-out-to-home-computers), I wrote we leverage [WireGuard](https://www.wireguard.com/) to bring home computers. We can get help from WireGuard to solve this PyTorch situation too. Once we set up a WireGuard network on every Docker containers, they communicate with one another via a single UDP port. This journey starts here.

### WireGuard Setup

Setting up a WireGuard network is pretty easy.  I skip the detailed setup steps in this article.  Basically you need two things: 1) create a conf file like /etc/wireguard/wg0.conf and run `wg-quick up wg0`.  Besides, when you create a Docker container, you need to specify `--cap-add=NET_ADMIN` and publish a UDP port.  Lastly, you need to install the packages `wireguard-tools iptables iproute2`.  The wireguard driver exists in the host as a part of Linux kernel, but you still need client tools to use it.

Once setup is done, you will see a virtual interface like `wg0`.

```log
root@2f3d44d8b0ea:/app# ip -4 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0@if32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default  link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 10.100.10.1/24 scope global wg0
       valid_lft forever preferred_lft forever
```

### Debugging with GDB

Let's run `torchrun` with the WireGuard network address 10.100.10.1 as the rendezvous point.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun \
  --nnodes=1 --nproc_per_node=1 --node_rank=0 \
  --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 \
  train.py
I0406 17:54:35.221000 1339 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 17:54:35.316000 1339 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_q6zcchrr
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 17:54:35.317000 1339 torch/distributed/launcher/api.py:224]
[E406 17:55:24.823650196 socket.cpp:1028] [c10d] The client socket has timed out after 60000ms while trying to connect to (10.100.10.1, 29500).
[W406 17:55:24.824122039 TCPStore.cpp:340] [c10d] TCP client failed to connect/validate to host 10.100.10.1:29500 - retrying (try=0, timeout=60000ms, delay=46179ms): The client socket has timed out after 60000ms while trying to connect to (10.100.10.1, 29500).
Exception raised from throwTimeoutError at /pytorch/torch/csrc/distributed/c10d/socket.cpp:1030 (most recent call first):
frame #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) + 0x9d (0x71c56a97205d in /usr/local/lib/python3.12/dist-packages/torch/lib/libc10.so)
frame #1: <unknown function> + 0x16daa1c (0x71c4c09eea1c in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #2: <unknown function> + 0x6b25497 (0x71c4c5e39497 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #3: <unknown function> + 0x6b256cf (0x71c4c5e396cf in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #4: <unknown function> + 0x6b25b57 (0x71c4c5e39b57 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #5: <unknown function> + 0x6a87113 (0x71c4c5d9b113 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #6: c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) + 0x41d (0x71c4c5da1ead in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #7: <unknown function> + 0xecbf51 (0x71c4d5872f51 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so)
frame #8: <unknown function> + 0x411060 (0x71c4d4db8060 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so)
frame #9: /usr/bin/python3() [0x581e9f]
frame #10: _PyObject_MakeTpCall + 0x13e (0x54903e in /usr/bin/python3)
...snip...
frame #29: <unknown function> + 0x2a1ca (0x71c59dc851ca in /lib/x86_64-linux-gnu/libc.so.6)
frame #30: __libc_start_main + 0x8b (0x71c59dc8528b in /lib/x86_64-linux-gnu/libc.so.6)
frame #31: _start + 0x25 (0x6576c5 in /usr/bin/python3)

[E406 17:56:55.628826474 socket.cpp:1028] [c10d] The client socket has timed out after 60000ms while trying to connect to (10.100.10.1, 29500).
[E406 17:56:55.628974143 TCPStore.cpp:328] [c10d] TCP client failed to connect/validate to host 10.100.10.1:29500 - timed out (try=1, timeout=60000ms): The client socket has timed out after 60000ms while trying to connect to (10.100.10.1, 29500).
Exception raised from throwTimeoutError at /pytorch/torch/csrc/distributed/c10d/socket.cpp:1030 (most recent call first):
frame #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) + 0x9d (0x71c56a97205d in /usr/local/lib/python3.12/dist-packages/torch/lib/libc10.so)
frame #1: <unknown function> + 0x16daa1c (0x71c4c09eea1c in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #2: <unknown function> + 0x6b25497 (0x71c4c5e39497 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #3: <unknown function> + 0x6b256cf (0x71c4c5e396cf in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #4: <unknown function> + 0x6b25b57 (0x71c4c5e39b57 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #5: <unknown function> + 0x6a87113 (0x71c4c5d9b113 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #6: c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) + 0x41d (0x71c4c5da1ead in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #7: <unknown function> + 0xecbf51 (0x71c4d5872f51 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so)
frame #8: <unknown function> + 0x411060 (0x71c4d4db8060 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so)
frame #9: /usr/bin/python3() [0x581e9f]
frame #10: _PyObject_MakeTpCall + 0x13e (0x54903e in /usr/bin/python3)
...snip...
frame #30: __libc_start_main + 0x8b (0x71c59dc8528b in /lib/x86_64-linux-gnu/libc.so.6)
frame #31: _start + 0x25 (0x6576c5 in /usr/bin/python3)

Traceback (most recent call last):
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/c10d_rendezvous_backend.py", line 156, in _create_tcp_store
    store = TCPStore(
            ^^^^^^^^^
torch.distributed.DistNetworkError: The client socket has timed out after 60000ms while trying to connect to (10.100.10.1, 29500).

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/torchrun", line 6, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/multiprocessing/errors/__init__.py", line 362, in wrapper
    return f(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/run.py", line 990, in main
    run(args)
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/run.py", line 981, in run
    elastic_launch(
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/launcher/api.py", line 170, in __call__
    return launch_agent(self._config, self._entrypoint, list(args))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/launcher/api.py", line 284, in launch_agent
    rdzv_handler=rdzv_registry.get_rendezvous_handler(rdzv_parameters),
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/registry.py", line 96, in get_rendezvous_handler
    return handler_registry.create_handler(params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/api.py", line 377, in create_handler
    handler = creator(params)
              ^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/registry.py", line 46, in _create_c10d_handler
    backend, store = create_backend(params)
                     ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/c10d_rendezvous_backend.py", line 254, in create_backend
    store = _create_tcp_store(params)
            ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/c10d_rendezvous_backend.py", line 180, in _create_tcp_store
    raise RendezvousConnectionError(
torch.distributed.elastic.rendezvous.api.RendezvousConnectionError: The connection to the C10d store has failed. See inner exception for details.
```

It failed with timeout! It seems that the process cannot connect to the rendezvous point 10.100.10.1:29500.

The log says `throwTimeoutError` was raised at [this line](https://github.com/pytorch/pytorch/blob/v2.11.0/torch/csrc/distributed/c10d/socket.cpp#L1030).  Is it time to file an issue there? No, it’s not what this blog does. It’s time to attach debugger!

As you may know, `torchrun` is just a python script to kick `torch.distributed.run`.

```log
root@2f3d44d8b0ea:/app# which torchrun
/usr/local/bin/torchrun
root@2f3d44d8b0ea:/app# cat /usr/local/bin/torchrun
#!/usr/bin/python3
import sys
from torch.distributed.run import main
if __name__ == '__main__':
    sys.argv[0] = sys.argv[0].removesuffix('.exe')
    sys.exit(main())
```

To debug it, you launch python with gdb, run the script, and break it when it’s stuck before the timeout exception is thrown.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 gdb -q /usr/bin/python3
Reading symbols from /usr/bin/python3...
(No debugging symbols found in /usr/bin/python3)
(gdb) set pagination off
(gdb) r /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 train.py
Starting program: /usr/bin/python3 /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 train.py
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7733f25ff6c0 (LWP 1528)]
...snip...
[New Thread 0x7733e55e56c0 (LWP 1554)]
I0406 18:11:48.680000 1525 torch/distributed/run.py:735] Using nproc_per_node=1.
[New Thread 0x7733dfe2a6c0 (LWP 1555)]
I0406 18:11:48.788000 1525 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_iju9cg3h
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 18:11:48.789000 1525 torch/distributed/launcher/api.py:224]
^C
Thread 1 "pt_elastic" received signal SIGINT, Interrupt.
0x0000773541c2dadf in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=0x7ffc61d9df30, rem=0x0)
    at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
warning: 78     ../sysdeps/unix/sysv/linux/clock_nanosleep.c: No such file or directory
(gdb) bt
#0  0x0000773541c2dadf in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=0x7ffc61d9df30, rem=0x0)
    at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
#1  0x0000773541c3aa27 in __GI___nanosleep (req=<optimized out>, rem=<optimized out>) at ../sysdeps/unix/sysv/linux/nanosleep.c:25
#2  0x0000773469c3849a in c10d::detail::(anonymous namespace)::SocketConnectOp::tryConnect(int) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#3  0x0000773469c396cf in c10d::detail::(anonymous namespace)::SocketConnectOp::run() [clone .constprop.0] ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#4  0x0000773469c39b57 in c10d::detail::Socket::connect(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, c10d::detail::SocketOptions const&) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#5  0x0000773469b9b113 in c10d::detail::TCPClient::connect(c10d::detail::SocketAddress const&, c10d::TCPStoreOptions const&, std::shared_ptr<c10d::Backoff>)
    () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#6  0x0000773469ba1ead in c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#7  0x0000773479672f51 in pybind11::cpp_function::initialize<pybind11::detail::initimpl::factory<torch::distributed::c10d::(anonymous namespace)::c10d_init(_object*, _object*)::{lambda(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool)#1}, pybind11::detail::void_type (*)(), c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool), pybind11::detail::void_type ()>::execute<pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >, pybind11::arg, pybind11::arg, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, char [24]>(pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >&, pybind11::arg const&, pybind11::arg const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, char const (&) [24]) &&::{lambda(pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool)#1}, void, pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool, pybind11::name, pybind11::is_method, pybind11::sibling, pybind11::detail::is_new_style_constructor, pybind11::arg, pybind11::arg, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, char [24]>(pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >&&, void (*)(pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool), pybind11::name const&, pybind11::is_method const&, pybind11::sibling const&, pybind11::detail::is_new_style_constructor const&, pybind11::arg const&, pybind11::arg const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, char const (&) [24])::{lambda(pybind11::detail::function_call&)#1}::_FUN(pybind11::detail::function_call&) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
#8  0x0000773478bb8060 in pybind11::cpp_function::dispatcher(_object*, _object*, _object*) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
...snip...
#28 0x00000000006bc3ed in Py_BytesMain ()
#29 0x0000773541b6b1ca in __libc_start_call_main (main=main@entry=0x518930, argc=argc@entry=9, argv=argv@entry=0x7ffc61d9fdf8)
    at ../sysdeps/nptl/libc_start_call_main.h:58
#30 0x0000773541b6b28b in __libc_start_main_impl (main=0x518930, argc=9, argv=0x7ffc61d9fdf8, init=<optimized out>, fini=<optimized out>,
    rtld_fini=<optimized out>, stack_end=0x7ffc61d9fde8) at ../csu/libc-start.c:360
#31 0x00000000006576c5 in _start ()
(gdb)
```

It’s running [`SocketConnectOp::tryConnect`](https://github.com/pytorch/pytorch/blob/v2.11.0/torch/csrc/distributed/c10d/socket.cpp#L824) , which calls the standard `connect` function via `tryConnectCore` , that is expected to fail.  Let’s double check. You can just set a breakpoint there.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 gdb -q /usr/bin/python3
Reading symbols from /usr/bin/python3...
(No debugging symbols found in /usr/bin/python3)
(gdb) set pagination off
(gdb) b SocketConnectOp::tryConnect
Function "SocketConnectOp::tryConnect" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (SocketConnectOp::tryConnect) pending.
(gdb) r /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 train.py
Starting program: /usr/bin/python3 /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 train.py
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x73fda9dff6c0 (LWP 1786)]
...
[New Thread 0x73fd9cde56c0 (LWP 1812)]
I0406 19:03:29.615000 1783 torch/distributed/run.py:735] Using nproc_per_node=1.
[New Thread 0x73fd977156c0 (LWP 1813)]
I0406 19:03:29.701000 1783 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_wofb5igg
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 19:03:29.702000 1783 torch/distributed/launcher/api.py:224]

Thread 1 "pt_elastic" hit Breakpoint 1, 0x000073fe214380b0 in c10d::detail::(anonymous namespace)::SocketConnectOp::tryConnect(int) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
(gdb) b connect
Breakpoint 2 at 0x73fe2139b070 (17 locations)
(gdb) c
Continuing.

Thread 1 "pt_elastic" hit Breakpoint 2.17, __libc_connect (fd=24, addr=addr@entry=..., len=len@entry=16) at ../sysdeps/unix/sysv/linux/connect.c:24
warning: 24     ../sysdeps/unix/sysv/linux/connect.c: No such file or directory
(gdb) bt
#0  __libc_connect (fd=24, addr=addr@entry=..., len=len@entry=16) at ../sysdeps/unix/sysv/linux/connect.c:24
#1  0x000073fef94f7d04 in reopen (statp=statp@entry=0x73fef95b8680 <_res>, terrno=terrno@entry=0x7ffd0220c058, ns=ns@entry=0) at ./resolv/res_send.c:856
#2  0x000073fef94f88ee in send_dg (ansp2_malloced=<optimized out>, resplen2=<optimized out>, anssizp2=<optimized out>, ansp2=<optimized out>,
    anscp=<optimized out>, gotsomewhere=<synthetic pointer>, v_circuit=<synthetic pointer>, ns=<optimized out>, terrno=0x7ffd0220c058,
    anssizp=0x7ffd0220c190, ansp=0x7ffd0220c048, buflen2=<optimized out>, buf2=<optimized out>, buflen=<optimized out>, buf=<optimized out>,
    statp=<optimized out>) at ./resolv/res_send.c:957
#3  __GI___res_context_send (ctx=ctx@entry=0x187d3590, buf=buf@entry=0x7ffd0220c240 "\267\252\001", buflen=<optimized out>, buf2=buf2@entry=0x0,
    buflen2=buflen2@entry=0, ans=<optimized out>, ans@entry=0x7ffd0220ca80 "", anssiz=<optimized out>, ansp=<optimized out>, ansp2=<optimized out>,
    nansp2=<optimized out>, resplen2=<optimized out>, ansp2_malloced=<optimized out>) at ./resolv/res_send.c:373
#4  0x000073fef94f6217 in __GI___res_context_query (ctx=ctx@entry=0x187d3590, name=name@entry=0x7ffd0220ce80 "1.10.100.10.in-addr.arpa", class=class@entry=1,
    type=type@entry=12, answer=answer@entry=0x7ffd0220ca80 "", anslen=anslen@entry=1024, answerp=0x7ffd0220c718, answerp2=0x0, nanswerp2=0x0, resplen2=0x0,
    answerp2_malloced=0x0) at ./resolv/res_query.c:218
#5  0x000073fef94ef564 in __GI__nss_dns_gethostbyaddr2_r (addr=<optimized out>, len=<optimized out>, af=<optimized out>, result=0x7ffd0220d460,
    buffer=<optimized out>, buflen=<optimized out>, errnop=0x73fef93ac278, h_errnop=0x7ffd0220d444, ttlp=0x0) at nss_dns/dns-host.c:576
#6  0x000073fef94efa59 in __GI__nss_dns_gethostbyaddr_r (addr=<optimized out>, len=<optimized out>, af=<optimized out>, result=<optimized out>,
    buffer=<optimized out>, buflen=<optimized out>, errnop=0x73fef93ac278, h_errnop=0x7ffd0220d444) at nss_dns/dns-host.c:630
#7  0x000073fef9509f2c in __gethostbyaddr_r (addr=addr@entry=0x187b0d58, len=len@entry=16, type=type@entry=10, resbuf=resbuf@entry=0x7ffd0220d460,
    buffer=<optimized out>, buflen=<optimized out>, result=<optimized out>, h_errnop=<optimized out>) at ../nss/getXXbyYY_r.c:273
#8  0x000073fef950ba86 in gni_host_inet_name (addrlen=<optimized out>, flags=<optimized out>, hostlen=1025, host=0x7ffd0220df60 "", sa=0x187b0d50,
    tmpbuf=0x7ffd0220d4a0) at ./nss/getnameinfo.c:243
#9  gni_host_inet (addrlen=<optimized out>, flags=<optimized out>, hostlen=1025, host=0x7ffd0220df60 "", sa=0x187b0d50, tmpbuf=0x7ffd0220d4a0)
    at ./nss/getnameinfo.c:382
#10 gni_host (addrlen=<optimized out>, flags=<optimized out>, hostlen=<optimized out>, host=<optimized out>, sa=<optimized out>, tmpbuf=<optimized out>)
    at ./nss/getnameinfo.c:424
#11 __GI_getnameinfo (sa=0x187b0d50, addrlen=<optimized out>, host=0x7ffd0220df60 "", hostlen=1025, serv=0x7ffd0220dd70 "", servlen=32, flags=<optimized out>)
    at ./nss/getnameinfo.c:538
#12 0x000073fe214368bb in c10d::detail::formatSockAddr[abi:cxx11](sockaddr const*, unsigned int) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#13 0x000073fe2143c03d in void fmt::v12::detail::value<fmt::v12::context>::format_custom<addrinfo>(void*, fmt::v12::parse_context<char>&, fmt::v12::context&)
    () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#14 0x000073fe1c2fba18 in fmt::v12::vformat[abi:cxx11](fmt::v12::basic_string_view<char>, fmt::v12::basic_format_args<fmt::v12::context>) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#15 0x000073fe21436748 in c10d::detail::SocketImpl::SocketImpl(int, addrinfo const&) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#16 0x000073fe21438990 in c10d::detail::(anonymous namespace)::SocketConnectOp::tryConnect(int) ()
...
(gdb) c
Continuing.

Thread 1 "pt_elastic" hit Breakpoint 2.17, __libc_connect (fd=23, addr=..., len=28) at ../sysdeps/unix/sysv/linux/connect.c:24
24      in ../sysdeps/unix/sysv/linux/connect.c
(gdb) bt
#0  __libc_connect (fd=23, addr=..., len=28) at ../sysdeps/unix/sysv/linux/connect.c:24
#1  0x000073fe214389e3 in c10d::detail::(anonymous namespace)::SocketConnectOp::tryConnect(int) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#2  0x000073fe214396cf in c10d::detail::(anonymous namespace)::SocketConnectOp::run() [clone .constprop.0] ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#3  0x000073fe21439b57 in c10d::detail::Socket::connect(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, c10d::detail::SocketOptions const&) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#4  0x000073fe2139b113 in c10d::detail::TCPClient::connect(c10d::detail::SocketAddress const&, c10d::TCPStoreOptions const&, std::shared_ptr<c10d::Backoff>)
    () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#5  0x000073fe213a1ead in c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#6  0x000073fe30e72f51 in pybind11::cpp_function::initialize<pybind11::detail::initimpl::factory<torch::distributed::c10d::(anonymous namespace)::c10d_init(_object*, _object*)::{lambda(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool)#1}, pybind11::detail::void_type (*)(), c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool), pybind11::detail::void_type ()>::execute<pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >, pybind11::arg, pybind11::arg, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, char [24]>(pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >&, pybind11::arg const&, pybind11::arg const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, char const (&) [24]) &&::{lambda(pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool)#1}, void, pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool, pybind11::name, pybind11::is_method, pybind11::sibling, pybind11::detail::is_new_style_constructor, pybind11::arg, pybind11::arg, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, char [24]>(pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >&&, void (*)(pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool), pybind11::name const&, pybind11::is_method const&, pybind11::sibling const&, pybind11::detail::is_new_style_constructor const&, pybind11::arg const&, pybind11::arg const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, char const (&) [24])::{lambda(pybind11::detail::function_call&)#1}::_FUN(pybind11::detail::function_call&) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
#7  0x000073fe303b8060 in pybind11::cpp_function::dispatcher(_object*, _object*, _object*) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
...
(gdb) b *0x000073fe214389e3
Breakpoint 3 at 0x73fe214389e3
(gdb) c
Continuing.

Thread 1 "pt_elastic" hit Breakpoint 3, 0x000073fe214389e3 in c10d::detail::(anonymous namespace)::SocketConnectOp::tryConnect(int) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
(gdb) p $eax
$1 = -1
(gdb) p (int)errno
$2 = 115

```

It hit twice. The first one is from `getnameinfo`, which looks unrelated and we skip. The second one was called from `SocketConnectOp::tryConnect` and failed with `EINPROGRESS` (= 115).  This is the one we're interested in. And we’re interested in the parameters of `connect`. Here’s assembly of where we are, immediately after the call to `connect`.

```log
(gdb) disas 0x000073fe214380b0
...
   0x000073fe214389ca <+2330>:  mov    -0x5a8(%rbp),%rax
   0x000073fe214389d1 <+2337>:  mov    0x10(%rax),%edx
   0x000073fe214389d4 <+2340>:  mov    0x18(%rax),%rsi
   0x000073fe214389d8 <+2344>:  mov    0x50(%rbx),%rax
   0x000073fe214389dc <+2348>:  mov    (%rax),%edi
   0x000073fe214389de <+2350>:  call   0x73fe1b9afb00 <connect@plt>
=> 0x000073fe214389e3 <+2355>:  test   %eax,%eax
```

What we want is the 2nd parameter `const struct sockaddr *addr`, which is passed via `$rsi`  in [System V ABI](https://wiki.osdev.org/System_V_ABI).  Since we already lost `$rdi`, we need restore it from the stack.

```log
(gdb) x/1g $rbp-0x5a8
0x7ffd0220e7c8: 0x00000000187b0d20
(gdb) x/1g 0x00000000187b0d20+0x18
0x187b0d38:     0x00000000187b0d50
(gdb) x/28xb 0x00000000187b0d50
0x187b0d50:     0x0a    0x00    0x73    0x3c    0x00    0x00    0x00    0x00
0x187b0d58:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x187b0d60:     0x00    0x00    0xff    0xff    0x0a    0x64    0x0a    0x01
0x187b0d68:     0x00    0x00    0x00    0x00
```

How to read this?  The address family is `0x0a 0x00` , which means `AF_INET6`, and we can see the address is `0xff 0xff 0x0a 0x64 0x0a 0x01`, which is an IPv4-mapped IPv6 address `::ffff:10.100.10.1`. The port is `0x73 0x3c` , which is 0x733c=29500.  This means the script simply tries to connect to the rendezvous point we specified, 10.100.10.1:29500, but it failed.  This means somebody should be listening on the endpoint.  Let's find out.

```
root@2f3d44d8b0ea:/app# ss -paln | grep 29500
root@2f3d44d8b0ea:/app#
```

Okay, nobody is listening on the endpoint, that's why `connect` failed.

The next thing to do is to see the positive behavior. We know this works with 127.0.0.1. Let’s see if the endpoint is listened on in that case.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 gdb -q /usr/bin/python3
Reading symbols from /usr/bin/python3...
(No debugging symbols found in /usr/bin/python3)
(gdb) set pagination off
(gdb) b SocketConnectOp::tryConnect
Function "SocketConnectOp::tryConnect" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (SocketConnectOp::tryConnect) pending.
(gdb) r /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=127.0.0.1:29500 train.py
Starting program: /usr/bin/python3 /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=127.0.0.1:29500 train.py
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7b4c309ff6c0 (LWP 1830)]
...
[New Thread 0x7b4c239e56c0 (LWP 1856)]
I0406 19:22:02.314000 1827 torch/distributed/run.py:735] Using nproc_per_node=1.
[New Thread 0x7b4c1e32a6c0 (LWP 1857)]
I0406 19:22:02.402000 1827 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 127.0.0.1:29500
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_2oqcd4j3
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 19:22:02.403000 1827 torch/distributed/launcher/api.py:224]
[New Thread 0x7b4c10dff6c0 (LWP 1858)]

Thread 1 "pt_elastic" hit Breakpoint 1, 0x00007b4ca80380b0 in c10d::detail::(anonymous namespace)::SocketConnectOp::tryConnect(int) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
(gdb)
```

And see this! The endpoint is there.

```
root@2f3d44d8b0ea:/app# ss -paln | grep 29500
tcp   LISTEN 0      4096                              *:29500                *:*    users:(("pt_elastic",pid=1827,fd=30))
```

So the problem is the script doesn’t start listening on the endpoint if the address is 10.100.10.1, while it does on 127.0.0.1.

Do you know which function to set a breakpoint on? Probably `listen` or `bind`?  Well, in this case, I did some homework for you already and it turned out `bind` was the one. So let’s do it.

```log
(gdb) del
Delete all breakpoints, watchpoints, tracepoints, and catchpoints? (y or n) y
(gdb) b bind
Breakpoint 2 at 0x7b4caa493780 (9 locations)
(gdb) r /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=127.0.0.1:29500 train.py
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /usr/bin/python3 /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=127.0.0.1:29500 train.py
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x70b1e8fff6c0 (LWP 1863)]
...
[New Thread 0x70b1dbfe56c0 (LWP 1889)]

Thread 1 "python3" hit Breakpoint 2.8, 0x000070b26fdda030 in torch::jit::slot_dict_impl<torch::jit::detail::ParameterPolicy>::bind(pybind11::module_ const&, char const*) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
(gdb) c
Continuing.

Thread 1 "python3" hit Breakpoint 2.7, 0x000070b26fdd9820 in torch::jit::slot_dict_impl<torch::jit::detail::BufferPolicy>::bind(pybind11::module_ const&, char const*) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
(gdb) c
Continuing.

Thread 1 "python3" hit Breakpoint 2.6, 0x000070b26fdd9010 in torch::jit::slot_dict_impl<torch::jit::detail::ModulePolicy>::bind(pybind11::module_ const&, char const*) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
(gdb) c
Continuing.
I0406 19:27:40.820000 1862 torch/distributed/run.py:735] Using nproc_per_node=1.
[New Thread 0x70b1d682a6c0 (LWP 1890)]

Thread 1 "pt_elastic" hit Breakpoint 2.9, __GI_bind () at ../sysdeps/unix/syscall-template.S:120
warning: 120    ../sysdeps/unix/syscall-template.S: No such file or directory
(gdb) bt
#0  __GI_bind () at ../sysdeps/unix/syscall-template.S:120
#1  0x000070b300eca4eb in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
#2  0x000070b300f18602 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
#3  0x000070b337a34a76 in ?? () from /usr/local/lib/python3.12/dist-packages/torch/lib/../../nvidia/cuda_runtime/lib/libcudart.so.12
#4  0x000070b337a39638 in ?? () from /usr/local/lib/python3.12/dist-packages/torch/lib/../../nvidia/cuda_runtime/lib/libcudart.so.12
#5  0x000070b338605ed3 in __pthread_once_slow (once_control=0x70b337cb2220, init_routine=0x70b337a395f0) at ./nptl/pthread_once.c:116
#6  0x000070b337a88ed9 in ?? () from /usr/local/lib/python3.12/dist-packages/torch/lib/../../nvidia/cuda_runtime/lib/libcudart.so.12
#7  0x000070b337a356ff in ?? () from /usr/local/lib/python3.12/dist-packages/torch/lib/../../nvidia/cuda_runtime/lib/libcudart.so.12
#8  0x000070b337a4d99a in cudaGetDeviceCount () from /usr/local/lib/python3.12/dist-packages/torch/lib/../../nvidia/cuda_runtime/lib/libcudart.so.12
#9  0x000070b3379ad842 in c10::cuda::device_count() () from /usr/local/lib/python3.12/dist-packages/torch/lib/libc10_cuda.so
#10 0x000070b26ffed402 in THCPModule_getDeviceCount_wrap(_object*, _object*) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
#11 0x0000000000581a8a in ?? ()
...
#25 0x00000000006bc3ed in Py_BytesMain ()
#26 0x000070b33858e1ca in __libc_start_call_main (main=main@entry=0x518930, argc=argc@entry=9, argv=argv@entry=0x7ffd9cdf2e68)
    at ../sysdeps/nptl/libc_start_call_main.h:58
#27 0x000070b33858e28b in __libc_start_main_impl (main=0x518930, argc=9, argv=0x7ffd9cdf2e68, init=<optimized out>, fini=<optimized out>,
    rtld_fini=<optimized out>, stack_end=0x7ffd9cdf2e58) at ../csu/libc-start.c:360
#28 0x00000000006576c5 in _start ()
(gdb) c
Continuing.
I0406 19:27:50.442000 1862 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 127.0.0.1:29500
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_lqlqv2qz
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 19:27:50.443000 1862 torch/distributed/launcher/api.py:224]

Thread 1 "pt_elastic" hit Breakpoint 2.9, __GI_bind () at ../sysdeps/unix/syscall-template.S:120
120     in ../sysdeps/unix/syscall-template.S

(gdb) bt
#0  __GI_bind () at ../sysdeps/unix/syscall-template.S:120
#1  0x000070b26b907b2d in uv.tcp_bind () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#2  0x000070b2605c0918 in c10d::detail::UvTcpServer::makeWithPort(uv_loop_s*, unsigned short, bool) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#3  0x000070b2605b78fe in c10d::detail::LibUVStoreDaemon::init(c10d::TCPStoreOptions const&) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#4  0x000070b2605b793f in c10d::detail::create_libuv_tcpstore_backend(c10d::TCPStoreOptions const&) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#5  0x000070b26059dc85 in c10d::detail::TCPServer::start(c10d::TCPStoreOptions const&) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#6  0x000070b2605a1bf2 in c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#7  0x000070b270072f51 in pybind11::cpp_function::initialize<pybind11::detail::initimpl::factory<torch::distributed::c10d::(anonymous namespace)::c10d_init(_object*, _object*)::{lambda(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool)#1}, pybind11::detail::void_type (*)(), c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool), pybind11::detail::void_type ()>::execute<pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >, pybind11::arg, pybind11::arg, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, char [24]>(pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >&, pybind11::arg const&, pybind11::arg const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, char const (&) [24]) &&::{lambda(pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool)#1}, void, pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool, pybind11::name, pybind11::is_method, pybind11::sibling, pybind11::detail::is_new_style_constructor, pybind11::arg, pybind11::arg, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, char [24]>(pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >&&, void (*)(pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool), pybind11::name const&, pybind11::is_method const&, pybind11::sibling const&, pybind11::detail::is_new_style_constructor const&, pybind11::arg const&, pybind11::arg const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, char const (&) [24])::{lambda(pybind11::detail::function_call&)#1}::_FUN(pybind11::detail::function_call&) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
#8  0x000070b26f5b8060 in pybind11::cpp_function::dispatcher(_object*, _object*, _object*) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
...
#28 0x00000000006bc3ed in Py_BytesMain ()
#29 0x000070b33858e1ca in __libc_start_call_main (main=main@entry=0x518930, argc=argc@entry=9, argv=argv@entry=0x7ffd9cdf2e68)
    at ../sysdeps/nptl/libc_start_call_main.h:58
#30 0x000070b33858e28b in __libc_start_main_impl (main=0x518930, argc=9, argv=0x7ffd9cdf2e68, init=<optimized out>, fini=<optimized out>,
    rtld_fini=<optimized out>, stack_end=0x7ffd9cdf2e58) at ../csu/libc-start.c:360
#31 0x00000000006576c5 in _start ()
```

Okay, we got it. In this positive scenario, we start listening on the endpoint through `TCPServer::start`.

Now, what happens if the address is 10.100.10.1? If you look at the debugger output earlier carefully, we called `connect` inside `TCPStore::TCPStore` through `TCPClient::connect`. So we know `TCPStore::TCPStore` is surely called. Let’s see if we call `TCPServer::start` or not.

```log
(gdb) del
Delete all breakpoints, watchpoints, tracepoints, and catchpoints? (y or n) y
(gdb) b TCPStore::TCPStore
Breakpoint 3 at 0x70b2605a1a90
(gdb) b TCPServer::start
Breakpoint 4 at 0x70b26059d990
(gdb) b TCPClient::connect
Breakpoint 5 at 0x70b26059b070
(gdb) r /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 train.py
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /usr/bin/python3 /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 train.py
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7b62f01ff6c0 (LWP 1900)]
...
[New Thread 0x7b62e31e56c0 (LWP 1926)]
I0406 19:36:25.975000 1899 torch/distributed/run.py:735] Using nproc_per_node=1.
[New Thread 0x7b62dda2a6c0 (LWP 1927)]
I0406 19:36:26.088000 1899 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_e8gckjjs
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 19:36:26.091000 1899 torch/distributed/launcher/api.py:224]

Thread 1 "pt_elastic" hit Breakpoint 3, 0x00007b63677a1a90 in c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
(gdb) c
Continuing.

Thread 1 "pt_elastic" hit Breakpoint 5, 0x00007b636779b070 in c10d::detail::TCPClient::connect(c10d::detail::SocketAddress const&, c10d::TCPStoreOptions const&, std::shared_ptr<c10d::Backoff>) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
(gdb)
```

See? We reached `TCPClient::connect` without hitting `TCPServer::start`. This is the problem. Let’s go check the code and see how we call `TCPServer::start`.  Code is [here](https://github.com/pytorch/pytorch/blob/v2.11.0/torch/csrc/distributed/c10d/TCPStore.cpp#L269).  It’s behind the check `if (opts.isServer) {`.  Does this mean `isServer` was `false` in our case?  Let's confirm it on debugger.

```log
(gdb) del
Delete all breakpoints, watchpoints, tracepoints, and catchpoints? (y or n) y
(gdb) b TCPStore::TCPStore
Breakpoint 6 at 0x7b63677a1a90
(gdb) r /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 train.py
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /usr/bin/python3 /usr/local/bin/torchrun --nnodes=1 --nproc_per_node=1 --node_rank=0 --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 train.py
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x71c1955ff6c0 (LWP 1929)]
...
[New Thread 0x71c1885e56c0 (LWP 1955)]
I0406 19:39:42.387000 1928 torch/distributed/run.py:735] Using nproc_per_node=1.
[New Thread 0x71c182f156c0 (LWP 1956)]
I0406 19:39:42.502000 1928 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_d435hy84
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 19:39:42.505000 1928 torch/distributed/launcher/api.py:224]

Thread 1 "pt_elastic" hit Breakpoint 6, 0x000071c20cba1a90 in c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
(gdb) disas $rip
Dump of assembler code for function _ZN4c10d8TCPStoreC2ENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKNS_15TCPStoreOptionsE:
=> 0x000071c20cba1a90 <+0>:     push   %rbp
   0x000071c20cba1a91 <+1>:     pxor   %xmm0,%xmm0
...
   0x000071c20cba1bbf <+303>:   call   0x71c20cc32ba0 <_ZN4c10d6detail6Socket10initializeEv> <<<< Socket::initialize()
   0x000071c20cba1bc4 <+308>:   mov    -0x608(%rbp),%rsi
   0x000071c20cba1bcb <+315>:   movzwl (%rsi),%eax
   0x000071c20cba1bce <+318>:   cmpb   $0x0,0x2(%rsi) <<<< opts.isServer
   0x000071c20cba1bd2 <+322>:   mov    %ax,0x38(%rbx)
   0x000071c20cba1bd6 <+326>:   je     0x71c20cba1df1 <_ZN4c10d8TCPStoreC2ENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERKNS_15TCPStoreOptionsE+865>
   0x000071c20cba1bdc <+332>:   lea    -0x240(%rbp),%rax
   0x000071c20cba1be3 <+339>:   mov    %rax,%rdi
   0x000071c20cba1be6 <+342>:   mov    %rax,-0x620(%rbp)
   0x000071c20cba1bed <+349>:   call   0x71c20cb9d990 <_ZN4c10d6detail9TCPServer5startERKNS_15TCPStoreOptionsE> <<<< TCPServer::start
   0x000071c20cba1bf2 <+354>:   mov    0x48(%rbx),%rdi
   0x000071c20cba1bf6 <+358>:   movdqa -0x240(%rbp),%xmm3
   0x000071c20cba1bfe <+366>:   movups %xmm3,0x40(%rbx)
   0x000071c20cba1c02 <+370>:   test   %rdi,%rdi
...
```

As I commented inline, `cmpb $0x0,0x2(%rsi)` is checking the flag.

```
(gdb) b *0x000071c20cba1bce
Breakpoint 7 at 0x71c20cba1bce
(gdb) c
Continuing.

Thread 1 "pt_elastic" hit Breakpoint 7, 0x000071c20cba1bce in c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
(gdb) x/1b $rsi+2
0x7ffcc728a192: 0
```

It’s zero! This is why we skip listening on the rendezvous point!

And it’s from the parameter `opts`. Where does it come from?  Who instantiates `TCPStore` ?

```log
(gdb) bt
#0  0x000071c20cba1bce in c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) () from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so
#1  0x000071c21c672f51 in pybind11::cpp_function::initialize<pybind11::detail::initimpl::factory<torch::distributed::c10d::(anonymous namespace)::c10d_init(_object*, _object*)::{lambda(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool)#1}, pybind11::detail::void_type (*)(), c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool), pybind11::detail::void_type ()>::execute<pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >, pybind11::arg, pybind11::arg, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, char [24]>(pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >&, pybind11::arg const&, pybind11::arg const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, char const (&) [24]) &&::{lambda(pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool)#1}, void, pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool, pybind11::name, pybind11::is_method, pybind11::sibling, pybind11::detail::is_new_style_constructor, pybind11::arg, pybind11::arg, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, pybind11::arg_v, char [24]>(pybind11::class_<c10d::TCPStore, c10::intrusive_ptr<c10d::TCPStore, c10::detail::intrusive_target_default_null_type<c10d::TCPStore> > >&&, void (*)(pybind11::detail::value_and_holder&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned short, std::optional<int>, bool, std::chrono::duration<long, std::ratio<1l, 1000l> >, bool, bool, std::optional<int>, bool), pybind11::name const&, pybind11::is_method const&, pybind11::sibling const&, pybind11::detail::is_new_style_constructor const&, pybind11::arg const&, pybind11::arg const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, pybind11::arg_v const&, char const (&) [24])::{lambda(pybind11::detail::function_call&)#1}::_FUN(pybind11::detail::function_call&) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
#2  0x000071c21bbb8060 in pybind11::cpp_function::dispatcher(_object*, _object*, _object*) ()
   from /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so
#3  0x0000000000581e9f in ?? ()
```

The symbol of frame #1 is insanely long.  See `pybind11` namespace. It’s python binding, meaning it’s python script instantiating `TCPStore` in C++.  `opts.isServer`  is also from python.

### Debugging with PDB

How to debug Python? Do we need VSCode or some fancy IDE to debug it? That’s not what this blog does.  Do we debug python binding?  Well, that's too ambitious.

One of the great functionality Python provides, compared to other script languages, is Python has a built-in console debugger, [pdb](https://docs.python.org/3/library/pdb.html). You don’t need additional components to live debug python code.  Very handy.

To use pdb, you modify your script to break at the beginning. Since we’re running our script with `torchrun`, we make a little modification in `torchrun` itself, just adding one line `breakpoint()` before `main()`.

```python
root@2f3d44d8b0ea:/app# cat /usr/local/bin/torchrun
#!/usr/bin/python3
import sys
from torch.distributed.run import main
if __name__ == '__main__':
    breakpoint() # <---- add this line
    sys.argv[0] = sys.argv[0].removesuffix('.exe')
    sys.exit(main())
```

Where does it call C++? A formal approach would be to start from the log `Starting elastic_operator with launch configs:`, which is from [this line](https://github.com/pytorch/pytorch/blob/v2.11.0/torch/distributed/launcher/api.py#L225). In our case, however, let’s simply search the repo for the keyword `TCPStore` .  This function [`_create_tcp_store`](https://github.com/pytorch/pytorch/blob/v2.11.0/torch/distributed/elastic/rendezvous/c10d_rendezvous_backend.py#L156) looks suspicious.  Since python is a script language, you can set a breakpoint accurately with a source line.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun   --nnodes=1 --nproc_per_node=1 --node_rank=0   --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500   train.py
> /usr/local/bin/torchrun(6)<module>()
-> sys.argv[0] = sys.argv[0].removesuffix('.exe')
(Pdb) b /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/c10d_rendezvous_backend.py:154
Breakpoint 1 at /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/c10d_rendezvous_backend.py:154
(Pdb) c
I0406 20:29:58.666000 1992 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 20:29:58.778000 1992 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_fw4c6ft_
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 20:29:58.781000 1992 torch/distributed/launcher/api.py:224]
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/c10d_rendezvous_backend.py(154)_create_tcp_store()
-> for is_server in [is_host, False]:
(Pdb) bt
  /usr/local/bin/torchrun(7)<module>()
-> sys.exit(main())
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/multiprocessing/errors/__init__.py(362)wrapper()
-> return f(*args, **kwargs)
  /usr/local/lib/python3.12/dist-packages/torch/distributed/run.py(990)main()
-> run(args)
  /usr/local/lib/python3.12/dist-packages/torch/distributed/run.py(981)run()
-> elastic_launch(
  /usr/local/lib/python3.12/dist-packages/torch/distributed/launcher/api.py(170)__call__()
-> return launch_agent(self._config, self._entrypoint, list(args))
  /usr/local/lib/python3.12/dist-packages/torch/distributed/launcher/api.py(284)launch_agent()
-> rdzv_handler=rdzv_registry.get_rendezvous_handler(rdzv_parameters),
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/registry.py(96)get_rendezvous_handler()
-> return handler_registry.create_handler(params)
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/api.py(377)create_handler()
-> handler = creator(params)
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/registry.py(46)_create_c10d_handler()
-> backend, store = create_backend(params)
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/c10d_rendezvous_backend.py(254)create_backend()
-> store = _create_tcp_store(params)
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/c10d_rendezvous_backend.py(154)_create_tcp_store()
-> for is_server in [is_host, False]:
(Pdb) p is_host
False
(Pdb) p cfg_is_host
None
(Pdb) p host
'10.100.10.1'
```

Okay, we got it. `_create_tcp_store` is instantiating `TCPStore` with `is_master=is_server`, which is `False`.

Where does this `is_host` come from? Since `cfg_is_host` is None , it must be from [`_matches_machine_hostname`](https://github.com/pytorch/pytorch/blob/v2.11.0/torch/distributed/elastic/rendezvous/utils.py#L117).  Let’s run this function line by line.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun   --nnodes=1 --nproc_per_node=1 --node_rank=0   --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500   train.py
> /usr/local/bin/torchrun(6)<module>()
-> sys.argv[0] = sys.argv[0].removesuffix('.exe')
(Pdb) b /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/utils.py:146
Breakpoint 1 at /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/utils.py:146
(Pdb) c
I0406 20:35:35.452000 2021 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 20:35:35.549000 2021 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_dek76zg_
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 20:35:35.551000 2021 torch/distributed/launcher/api.py:224]
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/utils.py(146)_matches_machine_hostname()
-> if host == this_host:
(Pdb) p host
'10.100.10.1'
(Pdb) p this_host
'2f3d44d8b0ea'
(Pdb) n
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/utils.py(149)_matches_machine_hostname()
-> addr_list = socket.getaddrinfo(
(Pdb)
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/utils.py(150)_matches_machine_hostname()
-> this_host, None, proto=socket.IPPROTO_TCP, flags=socket.AI_CANONNAME
(Pdb)
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/utils.py(149)_matches_machine_hostname()
-> addr_list = socket.getaddrinfo(
(Pdb)
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/utils.py(152)_matches_machine_hostname()
-> for addr_info in addr_list:
(Pdb) p addr_list
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '2f3d44d8b0ea', ('172.17.0.2', 0))]
(Pdb)
```

We clearly see the problem. First, we get the hostname with `gethostname()`, which is `2f3d44d8b0ea` (It matches the container’s ID). And we get the IP address associated with the hostname via `getaddrinfo`, which returns only `172.17.0.2`, the one mapped to the default bridge interface (eth0). Since it doesn’t match `10.100.10.1`, it thinks “I’m not the master. Somebody else should start listening on the rendezvous endpoint.”

Ideally `_matches_machine_hostname` should iterate all IP addressed assigned to check any of them matches the host. We may consider sending a PR to them, but for now, is there a good way to work around this behavior?

There is. See the beginning of `_create_tcp_store`. It’s overwritable through `cfg_is_host`, coming from `params`.

```python
    cfg_is_host = params.get_as_bool("is_host")
    # If the user has explicitly specified whether our process should host the
    # the store, respect it.
    if cfg_is_host is not None:
        is_host = cfg_is_host
    # Otherwise try to determine whether we are the host based on our hostname
    # and IP address.
    else:
        is_host = _matches_machine_hostname(host)
```

Where is this `RendezvousParameters` created? It’s way above `_matches_machine_hostname`. It’s in the function [`run`](https://github.com/pytorch/pytorch/blob/v2.11.0/torch/distributed/run.py#L980). `torchrun` takes a rarely-used parameter `--rdzv_conf` where we can specify key-value pairs. What we want to specify is `is_host`. This looks promising. Let’s try it.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun \
  --nnodes=1 --nproc_per_node=1 --node_rank=0 \
  --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 \
  --rdzv_conf=is_host=1 \
  train.py
I0406 20:52:48.947000 2060 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 20:52:49.029000 2060 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'is_host': 'True', 'timeout': 900}
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_ccaq0pji
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 20:52:49.030000 2060 torch/distributed/launcher/api.py:224]
I0406 20:52:49.055000 2060 torch/distributed/elastic/agent/server/api.py:898] [default] starting workers for entrypoint: python3
I0406 20:52:49.056000 2060 torch/distributed/elastic/agent/server/api.py:693] [default] Rendezvous'ing worker group
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539] [default] Rendezvous complete for workers. Result:
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   restart_count=0
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   master_addr=2f3d44d8b0ea
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   master_port=36587
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   group_rank=0
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   group_world_size=1
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   local_ranks=[0]
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   role_ranks=[0]
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   global_ranks=[0]
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   role_world_sizes=[1]
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   global_world_sizes=[1]
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]   event_log_handler=null
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:539]
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/api.py:701] [default] Starting worker group
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/local_elastic_agent.py:299] use_agent_store: True
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/local_elastic_agent.py:195] Environment variable 'TORCHELASTIC_ENABLE_FILE_TIMER' not found. Do not start FileTimerServer.
I0406 20:52:49.283000 2060 torch/distributed/elastic/agent/server/local_elastic_agent.py:239] Environment variable 'TORCHELASTIC_HEALTH_CHECK_PORT' not found. Do not start health check.
2f3d44d8b0ea:2092:2092 [0] NCCL INFO ENV/Plugin: Could not find: libnccl-env.so
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Bootstrap: Using eth0:172.17.0.2<0>
2f3d44d8b0ea:2092:2092 [0] NCCL INFO cudaDriverVersion 12080
2f3d44d8b0ea:2092:2092 [0] NCCL INFO NCCL version 2.28.9+cuda12.9
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Comm config Blocking set to 1
2f3d44d8b0ea:2092:2092 [0] NCCL INFO NET/Plugin: Could not find: libnccl-net.so
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Failed to open libibverbs.so[.1]
2f3d44d8b0ea:2092:2092 [0] NCCL INFO transport/net_ib.cc:852 -> 3
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Failed to initialize NET plugin IB
2f3d44d8b0ea:2092:2092 [0] NCCL INFO NET/Socket : Using [0]eth0:172.17.0.2<0> [1]wg0:10.100.10.1<0>
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Initialized NET plugin Socket
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Assigned NET plugin Socket to comm
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Using network Socket
2f3d44d8b0ea:2092:2092 [0] NCCL INFO ncclCommInitRankConfig comm 0x230fcf20 rank 0 nranks 1 cudaDev 0 nvmlDev 0 busId 70 commId 0xda9011adca28b7b9 - Init START
2f3d44d8b0ea:2092:2092 [0] NCCL INFO RAS client listening socket at ::1<28028>
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Bootstrap timings total 0.001490 (create 0.000058, send 0.000233, recv 0.000596, ring 0.000001, delay 0.000000)
2f3d44d8b0ea:2092:2092 [0] NCCL INFO NCCL_IGNORE_DISABLED_P2P set by environment to 1.
2f3d44d8b0ea:2092:2092 [0] NCCL INFO ncclTopoGetCpuAffinity: Affinity for GPU 0 is empty, ignoring. (GPU affinity =  ; CPU affinity = 0-27).
2f3d44d8b0ea:2092:2092 [0] NCCL INFO comm 0x230fcf20 rank 0 nRanks 1 nNodes 1 localRanks 1 localRank 0 MNNVL 0
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Channel 00/64 : 0
...
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Channel 63/64 : 0
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Trees [0] -1/-1/-1->0->-1 [1] -1/-1/-1->0->-1 [2] -1/-1/-1->0->-1 [3] -1/-1/-1->0->-1 [4] -1/-1/-1->0->-1 [5] -1/-1/-1->0->-1 [6] -1/-1/-1->0->-1 [7] -1/-1/-1->0->-1 [8] -1/-1/-1->0->-1 [9] -1/-1/-1->0->-1 [10] -1/-1/-1->0->-1 [11] -1/-1/-1->0->-1 [12] -1/-1/-1->0->-1 [13] -1/-1/-1->0->-1 [14] -1/-1/-1->0->-1 [15] -1/-1/-1->0->-1 [16] -1/-1/-1->0->-1 [17] -1/-1/-1->0->-1 [18] -1/-1/-1->0->-1 [19] -1/-1/-1->0->-1 [20] -1/-1/-1->0->-1 [21] -1/-1/-1->0->-1 [22] -1/-1/-1->0->-1 [23] -1/-1/-1->0->-1 [24] -1/-1/-1->0->-1 [25] -1/-1/-1->0->-1 [26] -1/-1/-1->0->-1 [27] -1/-1/-1->0->-1 [28] -1/-1/-1->0->-1 [29] -1/-1/-1->0->-1 [30] -1/-1/-1->0->-1 [31] -1/-1/-1->0->-1 [32] -1/-1/-1->0->-1 [33] -1/-1/-1->0->-1 [34] -1/-1/-1->0->-1 [35] -1/-1/-1->0->-1 [36] -1/-1/-1->0->-1 [37] -1/-1/-1->0->-1 [38] -1/-1/-1->0->-1 [39] -1/-1/-1->0->-1 [40] -1/-1/-1->0->-1 [41] -1/-1/-1->0->-1 [42] -1/-1/-1->0->-1 [43] -1/-1/-1->0->-1 [44] -1/-1/-1->0->-1 [45] -1/-1/-1->0->-1 [46] -1/-1/-1->0->-1 [47
2f3d44d8b0ea:2092:2092 [0] NCCL INFO P2P Chunksize set to 524288
2f3d44d8b0ea:2092:2092 [0] NCCL INFO PROFILER/Plugin: Could not find: libnccl-profiler.so
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Check P2P Type isAllDirectP2p 1 directMode 0 isAllCudaP2p 1
2f3d44d8b0ea:2092:2126 [0] NCCL INFO [Proxy Service] Device 0 CPU core 2
2f3d44d8b0ea:2092:2127 [0] NCCL INFO [Proxy Service UDS] Device 0 CPU core 12
2f3d44d8b0ea:2092:2092 [0] NCCL INFO TUNER/Plugin: Could not find: libnccl-tuner.so
2f3d44d8b0ea:2092:2092 [0] NCCL INFO 64 coll channels, 64 collnet channels, 0 nvls channels, 64 p2p channels, 64 p2p channels per peer
2f3d44d8b0ea:2092:2092 [0] NCCL INFO CC Off, workFifoBytes 1048576
2f3d44d8b0ea:2092:2092 [0] NCCL INFO ncclCommInitRankConfig comm 0x230fcf20 rank 0 nranks 1 cudaDev 0 nvmlDev 0 busId 70 commId 0xda9011adca28b7b9 - Init COMPLETE
2f3d44d8b0ea:2092:2092 [0] NCCL INFO Init timings - ncclCommInitRankConfig: rank 0 nranks 1 total 0.17 (kernels 0.13, alloc 0.00, bootstrap 0.00, allgathers 0.00, topo 0.00, graphs 0.00, connections 0.02, rest 0.00)
[Rank 0] Starting training...
Step 0 | Loss: 1.3868
Step 10 | Loss: 1.5847
Step 20 | Loss: 1.5615
[Rank 0] Training complete.
2f3d44d8b0ea:2092:2092 [0] NCCL INFO comm 0x230fcf20 rank 0 nranks 1 cudaDev 0 busId 70 - Destroy COMPLETE
2f3d44d8b0ea:2092:2092 [0] NCCL INFO ENV/Plugin: Closing env plugin ncclEnvDefault
I0406 20:52:52.994000 2060 torch/distributed/elastic/https://github.com/pytorch/pytorch/blob/v2.11.0/torch/distributed/run.py#L1006agent/server/api.py:917] [default] worker group successfully finished. Waiting 300 seconds for other agents to finish.
I0406 20:52:52.994000 2060 torch/distributed/elastic/agent/server/api.py:970] Local worker group finished (WorkerState.SUCCEEDED). Waiting 300 seconds for other agents to finish
I0406 20:52:52.995000 2060 torch/distributed/elastic/agent/server/api.py:984] Done waiting for other agents. Elapsed: 0.00042891502380371094 seconds
root@2f3d44d8b0ea:/app#
```

It worked like a charm!

Now, it’s time to run a training on two nodes?

### Training on two nodes

This is the first node; master node, rank 0.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun \
  --nnodes=2 --nproc_per_node=1 --node_rank=0 \
  --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 \
  --rdzv_conf=is_host=1 \
  train.py
I0406 20:59:43.723000 2129 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 20:59:43.825000 2129 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   min_nodes                : 2
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   max_nodes                : 2
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'is_host': 'True', 'timeout': 900}
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_ji8a86n0
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 20:59:43.826000 2129 torch/distributed/launcher/api.py:224]
I0406 20:59:43.854000 2129 torch/distributed/elastic/agent/server/api.py:898] [default] starting workers for entrypoint: python3
I0406 20:59:43.855000 2129 torch/distributed/elastic/agent/server/api.py:693] [default] Rendezvous'ing worker group
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539] [default] Rendezvous complete for workers. Result:
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   restart_count=0
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   master_addr=2f3d44d8b0ea
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   master_port=44403
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   group_rank=0
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   group_world_size=2
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   local_ranks=[0]
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   role_ranks=[0]
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   global_ranks=[0]
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   role_world_sizes=[2]
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   global_world_sizes=[2]
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]   event_log_handler=null
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:539]
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/api.py:701] [default] Starting worker group
I0406 20:59:51.030000 2129 torch/distributed/elastic/agent/server/local_elastic_agent.py:299] use_agent_store: True
I0406 20:59:51.031000 2129 torch/distributed/elastic/agent/server/local_elastic_agent.py:195] Environment variable 'TORCHELASTIC_ENABLE_FILE_TIMER' not found. Do not start FileTimerServer.
I0406 20:59:51.031000 2129 torch/distributed/elastic/agent/server/local_elastic_agent.py:239] Environment variable 'TORCHELASTIC_HEALTH_CHECK_PORT' not found. Do not start health check.
2f3d44d8b0ea:2161:2161 [0] NCCL INFO ENV/Plugin: Could not find: libnccl-env.so
2f3d44d8b0ea:2161:2161 [0] NCCL INFO Bootstrap: Using eth0:172.17.0.2<0>
2f3d44d8b0ea:2161:2161 [0] NCCL INFO cudaDriverVersion 12080
2f3d44d8b0ea:2161:2161 [0] NCCL INFO NCCL version 2.28.9+cuda12.9
2f3d44d8b0ea:2161:2161 [0] NCCL INFO Comm config Blocking set to 1
2f3d44d8b0ea:2161:2161 [0] NCCL INFO NET/Plugin: Could not find: libnccl-net.so
2f3d44d8b0ea:2161:2161 [0] NCCL INFO Failed to open libibverbs.so[.1]
2f3d44d8b0ea:2161:2161 [0] NCCL INFO transport/net_ib.cc:852 -> 3
2f3d44d8b0ea:2161:2161 [0] NCCL INFO Failed to initialize NET plugin IB
2f3d44d8b0ea:2161:2161 [0] NCCL INFO NET/Socket : Using [0]eth0:172.17.0.2<0> [1]wg0:10.100.10.1<0>
2f3d44d8b0ea:2161:2161 [0] NCCL INFO Initialized NET plugin Socket
2f3d44d8b0ea:2161:2161 [0] NCCL INFO Assigned NET plugin Socket to comm
2f3d44d8b0ea:2161:2161 [0] NCCL INFO Using network Socket
2f3d44d8b0ea:2161:2161 [0] NCCL INFO ncclCommInitRankConfig comm 0x3afce880 rank 0 nranks 2 cudaDev 0 nvmlDev 0 busId 70 commId 0x622b058bf1d66d35 - Init START
```

This is the second node; rank 1.  Ouch!  It failed!

```log
root@dd3bc9f2214d:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun \
  --nnodes=2 --nproc_per_node=1 --node_rank=1 \
  --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500 \
  train.py
I0406 20:59:49.909000 131 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   min_nodes                : 2
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   max_nodes                : 2
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_bxkvajbb
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 20:59:49.992000 131 torch/distributed/launcher/api.py:224]
I0406 20:59:50.023000 131 torch/distributed/elastic/agent/server/api.py:898] [default] starting workers for entrypoint: python3
I0406 20:59:50.023000 131 torch/distributed/elastic/agent/server/api.py:693] [default] Rendezvous'ing worker group
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539] [default] Rendezvous complete for workers. Result:
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   restart_count=0
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   master_addr=2f3d44d8b0ea
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   master_port=44403
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   group_rank=1
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   group_world_size=2
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   local_ranks=[0]
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   role_ranks=[1]
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   global_ranks=[1]
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   role_world_sizes=[2]
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   global_world_sizes=[2]
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]   event_log_handler=null
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:539]
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/api.py:701] [default] Starting worker group
I0406 20:59:51.032000 131 torch/distributed/elastic/agent/server/local_elastic_agent.py:299] use_agent_store: True
I0406 20:59:51.033000 131 torch/distributed/elastic/agent/server/local_elastic_agent.py:195] Environment variable 'TORCHELASTIC_ENABLE_FILE_TIMER' not found. Do not start FileTimerServer.
I0406 20:59:51.033000 131 torch/distributed/elastic/agent/server/local_elastic_agent.py:239] Environment variable 'TORCHELASTIC_HEALTH_CHECK_PORT' not found. Do not start health check.
[W406 20:59:52.360496683 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 20:59:52.816683350 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 20:59:53.625858136 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 20:59:54.305711072 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 20:59:56.854604631 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 20:59:58.498181734 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:00:02.234917616 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:00:10.139460584 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:00:17.403585181 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:00:25.605543230 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:00:36.109063693 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:01:06.103270816 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:01:32.333383773 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:02:48.901471959 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:03:22.483614915 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:04:02.944138445 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:05:13.293170306 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:06:36.582248302 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:07:41.396091809 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:08:12.654681204 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:08:54.086316393 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[E406 21:08:54.086357761 socket.cpp:1028] [c10d] The client socket has timed out after 600000ms while trying to connect to (2f3d44d8b0ea, 44403).
[W406 21:08:54.086697964 TCPStore.cpp:340] [c10d] TCP client failed to connect/validate to host 2f3d44d8b0ea:44403 - retrying (try=0, timeout=600000ms, delay=74560ms): The client socket has timed out after 600000ms while trying to connect to (2f3d44d8b0ea, 44403).
Exception raised from throwTimeoutError at /pytorch/torch/csrc/distributed/c10d/socket.cpp:1030 (most recent call first):
frame #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) + 0x9d (0x7cadc417205d in /usr/local/lib/python3.12/dist-packages/torch/lib/libc10.so)
frame #1: <unknown function> + 0x16daa1c (0x7cace79eea1c in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #2: <unknown function> + 0x6b25497 (0x7cacece39497 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #3: <unknown function> + 0x6b256cf (0x7cacece396cf in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #4: <unknown function> + 0x6b25b57 (0x7cacece39b57 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #5: <unknown function> + 0x6a87113 (0x7cacecd9b113 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #6: c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) + 0x41d (0x7cacecda1ead in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #7: <unknown function> + 0xecbf51 (0x7cacfc872f51 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so)
frame #8: <unknown function> + 0x411060 (0x7cacfbdb8060 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so)
frame #9: /usr/bin/python3() [0x581e9f]
...
frame #32: _start + 0x25 (0x6576c5 in /usr/bin/python3)

[W406 21:10:08.720419645 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:10:48.462175738 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:11:42.498706496 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:12:12.849740252 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:12:43.260536804 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:13:41.678641445 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:14:14.137672492 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:15:41.308584356 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:16:48.530796444 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:17:19.359159001 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:18:45.552803370 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[W406 21:19:24.849525746 socket.cpp:764] [c10d] The IPv6 network addresses of (2f3d44d8b0ea, 44403) cannot be retrieved (gai error: -2 - Name or service not known).
[E406 21:19:24.849598964 socket.cpp:1028] [c10d] The client socket has timed out after 600000ms while trying to connect to (2f3d44d8b0ea, 44403).
[E406 21:19:24.849748526 TCPStore.cpp:328] [c10d] TCP client failed to connect/validate to host 2f3d44d8b0ea:44403 - timed out (try=1, timeout=600000ms): The client socket has timed out after 600000ms while trying to connect to (2f3d44d8b0ea, 44403).
Exception raised from throwTimeoutError at /pytorch/torch/csrc/distributed/c10d/socket.cpp:1030 (most recent call first):
frame #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) + 0x9d (0x7cadc417205d in /usr/local/lib/python3.12/dist-packages/torch/lib/libc10.so)
frame #1: <unknown function> + 0x16daa1c (0x7cace79eea1c in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #2: <unknown function> + 0x6b25497 (0x7cacece39497 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #3: <unknown function> + 0x6b256cf (0x7cacece396cf in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #4: <unknown function> + 0x6b25b57 (0x7cacece39b57 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #5: <unknown function> + 0x6a87113 (0x7cacecd9b113 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #6: c10d::TCPStore::TCPStore(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, c10d::TCPStoreOptions const&) + 0x41d (0x7cacecda1ead in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_cpu.so)
frame #7: <unknown function> + 0xecbf51 (0x7cacfc872f51 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so)
frame #8: <unknown function> + 0x411060 (0x7cacfbdb8060 in /usr/local/lib/python3.12/dist-packages/torch/lib/libtorch_python.so)
frame #9: /usr/bin/python3() [0x581e9f]
...
frame #31: __libc_start_main + 0x8b (0x7cadc4e2128b in /lib/x86_64-linux-gnu/libc.so.6)
frame #32: _start + 0x25 (0x6576c5 in /usr/bin/python3)

Traceback (most recent call last):
  File "/app/train.py", line 54, in <module>
    run_training()
  File "/app/train.py", line 18, in run_training
    setup()
  File "/app/train.py", line 11, in setup
    dist.init_process_group(backend="nccl")
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/c10d_logger.py", line 83, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/c10d_logger.py", line 97, in wrapper
    func_return = func(*args, **kwargs)
                  ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/distributed_c10d.py", line 1831, in init_process_group
    store, rank, world_size = next(rendezvous_iterator)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/rendezvous.py", line 280, in _env_rendezvous_handler
    store = _create_c10d_store(
            ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/rendezvous.py", line 190, in _create_c10d_store
    return TCPStore(
           ^^^^^^^^^
torch.distributed.DistNetworkError: The client socket has timed out after 600000ms while trying to connect to (2f3d44d8b0ea, 44403).
E0406 21:19:25.365000 131 torch/distributed/elastic/multiprocessing/api.py:986] failed (exitcode: 1) local_rank: 0 (pid: 161) of binary: /usr/bin/python3
I0406 21:19:25.376000 131 torch/distributed/elastic/multiprocessing/errors/__init__.py:375] ('local_rank %s FAILED with no error file. Decorate your entrypoint fn with @record for traceback info. See: https://pytorch.org/docs/stable/elastic/errors.html', 1)
Traceback (most recent call last):
  File "/usr/local/bin/torchrun", line 6, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/multiprocessing/errors/__init__.py", line 362, in wrapper
    return f(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/run.py", line 990, in main
    run(args)
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/run.py", line 981, in run
    elastic_launch(
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/launcher/api.py", line 170, in __call__
    return launch_agent(self._config, self._entrypoint, list(args))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/launcher/api.py", line 317, in launch_agent
    raise ChildFailedError(
torch.distributed.elastic.multiprocessing.errors.ChildFailedError:
============================================================
train.py FAILED
------------------------------------------------------------
Failures:
  <NO_OTHER_FAILURES>
------------------------------------------------------------
Root Cause (first observed failure):
[0]:
  time      : 2026-04-06_21:19:25
  host      : dd3bc9f2214d
  rank      : 1 (local_rank: 0)
  exitcode  : 1 (pid: 161)
  error_file: <N/A>
  traceback : To enable traceback see: https://pytorch.org/docs/stable/elastic/errors.html
============================================================
root@dd3bc9f2214d:/app# e
```

The reason is obvious.  The message `TCP client failed to connect/validate to host 2f3d44d8b0ea:44403` implies It tried to communicate via hostname, which is not reachable.

Looking at the first node’s log carefully, you see `master_addr=2f3d44d8b0ea`. This means the first node advertised itself with the hostname. In this case, we want to use the IP address.

Let’s find out where `torchrun` prints this log. Well, it’s in the log: `torch/distributed/elastic/agent/server/api.py:539`, [here](https://github.com/pytorch/pytorch/blob/v2.11.0/torch/distributed/elastic/agent/server/api.py#L539)​.  The line `master_addr = spec.master_addr or rdzv_info.bootstrap_store_info.master_addr` does that. It’s easy to confirm it with pdb.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun   --nnodes=1 --nproc_per_node=1 --node_rank=0   --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500   --rdzv_conf=is_host=True   train.py
> /usr/local/bin/torchrun(6)<module>()
-> sys.argv[0] = sys.argv[0].removesuffix('.exe')
(Pdb) b /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/agent/server/api.py:524
Breakpoint 1 at /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/agent/server/api.py:524
(Pdb) c
I0406 21:51:17.139000 2208 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 21:51:17.236000 2208 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'is_host': 'True', 'timeout': 900}
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_qk97alc2
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 21:51:17.238000 2208 torch/distributed/launcher/api.py:224]
I0406 21:51:17.265000 2208 torch/distributed/elastic/agent/server/api.py:898] [default] starting workers for entrypoint: python3
I0406 21:51:17.267000 2208 torch/distributed/elastic/agent/server/api.py:693] [default] Rendezvous'ing worker group
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/agent/server/api.py(524)_rendezvous()
-> self._store = store
(Pdb) p spec.master_addr
None
(Pdb) p rdzv_info.bootstrap_store_info.master_addr
'2f3d44d8b0ea'
(Pdb)
```

It’s coming from `rdzv_info.bootstrap_store_info.master_addr`. Who set the hostname in it? We need to step in the line `spec.rdzv_handler.next_rendezvous()`.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun   --nnodes=1 --nproc_per_node=1 --node_rank=0   --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500   --rdzv_conf=is_host=True   train.py
> /usr/local/bin/torchrun(6)<module>()
-> sys.argv[0] = sys.argv[0].removesuffix('.exe')
(Pdb) b /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/agent/server/api.py:514
Breakpoint 1 at /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/agent/server/api.py:514
(Pdb) c
I0406 23:42:36.311000 2320 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 23:42:36.408000 2320 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'is_host': 'True', 'timeout': 900}
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_fv9249b9
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 23:42:36.411000 2320 torch/distributed/launcher/api.py:224]
I0406 23:42:36.443000 2320 torch/distributed/elastic/agent/server/api.py:898] [default] starting workers for entrypoint: python3
I0406 23:42:36.446000 2320 torch/distributed/elastic/agent/server/api.py:693] [default] Rendezvous'ing worker group
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/agent/server/api.py(514)_rendezvous()
-> rdzv_info = spec.rdzv_handler.next_rendezvous()
(Pdb) s
--Call--
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py(1148)next_rendezvous()
-> def next_rendezvous(self) -> RendezvousInfo:
(Pdb) b /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py:1206
Breakpoint 2 at /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py:1206
(Pdb) c
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py(1206)next_rendezvous()
-> if self._bootstrap_store_info is None:
(Pdb) p self._bootstrap_store_info
None
(Pdb) self._this_node.addr
'2f3d44d8b0ea'
```

Alright, we have the hostname in `self._this_node.addr`, which is passed to `RendezvousStoreInfo.build` to build `_bootstrap_store_info`. Who set `self._this_node`? It’s in [`DynamicRendezvousHandler.__init__`](https://github.com/pytorch/pytorch/blob/v2.11.0/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py#L1086).

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun   --nnodes=1 --nproc_per_node=1 --node_rank=0   --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500   --rdzv_conf=is_host=True   train.py
> /usr/local/bin/torchrun(6)<module>()
-> sys.argv[0] = sys.argv[0].removesuffix('.exe')
(Pdb) b /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py:1072
Breakpoint 1 at /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py:1072
(Pdb) c
I0406 23:46:37.870000 2351 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 23:46:37.976000 2351 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'is_host': 'True', 'timeout': 900}
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_8d10hlwv
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 23:46:37.979000 2351 torch/distributed/launcher/api.py:224]
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py(1072)__init__()
-> if not settings.run_id:
(Pdb) p node
2f3d44d8b0ea_2351_0
(Pdb) p node.addr
'2f3d44d8b0ea'
(Pdb) bt
  /usr/local/bin/torchrun(7)<module>()
-> sys.exit(main())
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/multiprocessing/errors/__init__.py(362)wrapper()
-> return f(*args, **kwargs)
  /usr/local/lib/python3.12/dist-packages/torch/distributed/run.py(990)main()
-> run(args)
  /usr/local/lib/python3.12/dist-packages/torch/distributed/run.py(981)run()
-> elastic_launch(
  /usr/local/lib/python3.12/dist-packages/torch/distributed/launcher/api.py(170)__call__()
-> return launch_agent(self._config, self._entrypoint, list(args))
  /usr/local/lib/python3.12/dist-packages/torch/distributed/launcher/api.py(284)launch_agent()
-> rdzv_handler=rdzv_registry.get_rendezvous_handler(rdzv_parameters),
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/registry.py(96)get_rendezvous_handler()
-> return handler_registry.create_handler(params)
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/api.py(377)create_handler()
-> handler = creator(params)
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/registry.py(48)_create_c10d_handler()
-> return create_handler(store, backend, params)
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py(1436)create_handler()
-> return DynamicRendezvousHandler.from_backend(
  /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py(1062)from_backend()
-> return cls(node, settings, backend.name, store, state_holder)
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py(1072)__init__()
-> if not settings.run_id:
(Pdb)
```

And `node` is coming from `cls._node_desc_generator.generate(local_addr)` in `from_backend`. Let’s see this `generate` function.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun   --nnodes=1 --nproc_per_node=1 --node_rank=0   --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500   --rdzv_conf=is_host=True   train.py
> /usr/local/bin/torchrun(6)<module>()
-> sys.argv[0] = sys.argv[0].removesuffix('.exe')
(Pdb) b /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py:1049
Breakpoint 1 at /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py:1049
(Pdb) c
I0406 23:48:24.537000 2381 torch/distributed/run.py:735] Using nproc_per_node=1.
I0406 23:48:24.633000 2381 torch/distributed/launcher/api.py:131] Using default numa options = None
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'is_host': 'True', 'timeout': 900}
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_9e8y2vz6
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   numa_options             : None
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0406 23:48:24.635000 2381 torch/distributed/launcher/api.py:224]
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py(1049)from_backend()
-> node = cls._node_desc_generator.generate(local_addr)
(Pdb) p local_addr
None
(Pdb) s
--Call--
> /usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py(261)generate()
-> def generate(self, local_addr: str | None = None) -> _NodeDesc:
(Pdb)
```

`local_addr` is `None`, and see the function [`generate`](https://github.com/pytorch/pytorch/blob/v2.11.0/torch/distributed/elastic/rendezvous/dynamic_rendezvous.py#L261). It does `local_addr or socket.getfqdn()`! This is where PyTorch prefers the hostname though the varialbe name is `local_addr`.

Now the question is how to overwrite it. In other words, how to set the IP address in this `local_addr`? We backtrack a little bit more. In `create_handler`, we pass `params.local_addr` to `DynamicRendezvousHandler.from_backend`. And `params` is `RendezvousParameters`. We already know this class, right? It’s where we store `is_host` earlier.

So we specify `--rdzv_conf=is_host=True,local_addr=10.100.10.1`?

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG STEPS=30 torchrun   --nnodes=1 --nproc_per_node=1 --node_rank=0   --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500   --rdzv_conf=is_host=True,local_addr=10.100.10.1   train.py
I0407 03:21:26.187000 2508 torch/distributed/run.py:735] Using nproc_per_node=1.
I0407 03:21:26.296000 2508 torch/distributed/launcher/api.py:131] Using default numa options = None
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   entrypoint               : train.py
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   min_nodes                : 1
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   max_nodes                : 1
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'is_host': 'True', 'local_addr': '10.100.10.1', 'timeout': 900}
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_zhdsgvr7
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   numa_options             : None
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0407 03:21:26.298000 2508 torch/distributed/launcher/api.py:224]
Traceback (most recent call last):
  File "/usr/local/bin/torchrun", line 6, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/elastic/multiprocessing/errors/__init__.py", line 362, in wrapper
    return f(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/run.py", line 990, in main
    run(args)
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/run.py", line 981, in run
    elastic_launch(
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/launcher/api.py", line 170, in __call__
    return launch_agent(self._config, self._entrypoint, list(args))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/dist-packages/torch/distributed/launcher/api.py", line 264, in launch_agent
    rdzv_parameters = RendezvousParameters(
                      ^^^^^^^^^^^^^^^^^^^^^
TypeError: torch.distributed.elastic.rendezvous.api.RendezvousParameters() got multiple values for keyword argument 'local_addr'
root@2f3d44d8b0ea:/app#
```

It was close, but failed with `TypeError: torch.distributed.elastic.rendezvous.api.RendezvousParameters() got multiple values for keyword argument 'local_addr'` because PyTorch instantiates `RendezvousParameters` as below. It explicitly specifies `local_addr` already, so we cannot include it in `config.rdzv_configs`.

```python
    rdzv_parameters = RendezvousParameters(
        backend=config.rdzv_backend,
        endpoint=config.rdzv_endpoint,
        run_id=config.run_id,
        min_nodes=config.min_nodes,
        max_nodes=config.max_nodes,
        local_addr=config.local_addr,
        **config.rdzv_configs,
    )
```

Where does `config.local_addr` come from? It’s from `args.local_addr` in `config_from_args`. `torchrun` directly takes `--local_addr` parameter.

Let’s try one more time! This is from the master node.

```log
root@2f3d44d8b0ea:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG NCCL_SOCKET_IFNAME=wg0 STEPS=30 torchrun   --nnodes=2 --nproc_per_node=1 --node_rank=0   --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500   --rdzv_conf=is_host=1   --local_addr=10.100.10.1   /app/train.py
I0407 03:33:29.820000 2911 torch/distributed/run.py:735] Using nproc_per_node=1.
I0407 03:33:29.926000 2911 torch/distributed/launcher/api.py:131] Using default numa options = None
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   entrypoint               : /app/train.py
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   min_nodes                : 2
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   max_nodes                : 2
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'is_host': '1', 'timeout': 900}
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_p9blggcp
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   numa_options             : None
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0407 03:33:29.928000 2911 torch/distributed/launcher/api.py:224]
I0407 03:33:29.956000 2911 torch/distributed/elastic/agent/server/api.py:898] [default] starting workers for entrypoint: python3
I0407 03:33:29.956000 2911 torch/distributed/elastic/agent/server/api.py:693] [default] Rendezvous'ing worker group
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539] [default] Rendezvous complete for workers. Result:
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   restart_count=0
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   master_addr=10.100.10.1
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   master_port=35121
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   group_rank=0
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   group_world_size=2
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   local_ranks=[0]
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   role_ranks=[0]
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   global_ranks=[0]
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   role_world_sizes=[2]
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   global_world_sizes=[2]
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]   event_log_handler=null
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:539]
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/api.py:701] [default] Starting worker group
I0407 03:33:39.096000 2911 torch/distributed/elastic/agent/server/local_elastic_agent.py:299] use_agent_store: True
I0407 03:33:39.097000 2911 torch/distributed/elastic/agent/server/local_elastic_agent.py:195] Environment variable 'TORCHELASTIC_ENABLE_FILE_TIMER' not found. Do not start FileTimerServer.
I0407 03:33:39.097000 2911 torch/distributed/elastic/agent/server/local_elastic_agent.py:239] Environment variable 'TORCHELASTIC_HEALTH_CHECK_PORT' not found. Do not start health check.
2f3d44d8b0ea:2943:2943 [0] NCCL INFO ENV/Plugin: Could not find: libnccl-env.so
2f3d44d8b0ea:2943:2943 [0] NCCL INFO NCCL_SOCKET_IFNAME set by environment to wg0
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Bootstrap: Using wg0:10.100.10.1<0>
2f3d44d8b0ea:2943:2943 [0] NCCL INFO cudaDriverVersion 12080
2f3d44d8b0ea:2943:2943 [0] NCCL INFO NCCL version 2.28.9+cuda12.9
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Comm config Blocking set to 1
2f3d44d8b0ea:2943:2943 [0] NCCL INFO NET/Plugin: Could not find: libnccl-net.so
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Failed to open libibverbs.so[.1]
2f3d44d8b0ea:2943:2943 [0] NCCL INFO transport/net_ib.cc:852 -> 3
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Failed to initialize NET plugin IB
2f3d44d8b0ea:2943:2943 [0] NCCL INFO NCCL_SOCKET_IFNAME set by environment to wg0
2f3d44d8b0ea:2943:2943 [0] NCCL INFO NET/Socket : Using [0]wg0:10.100.10.1<0>
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Initialized NET plugin Socket
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Assigned NET plugin Socket to comm
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Using network Socket
2f3d44d8b0ea:2943:2943 [0] NCCL INFO ncclCommInitRankConfig comm 0x271b62c0 rank 0 nranks 2 cudaDev 0 nvmlDev 0 busId 70 commId 0x77d5d096c29cef94 - Init START
2f3d44d8b0ea:2943:2943 [0] NCCL INFO RAS client listening socket at ::1<28028>
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Bootstrap timings total 0.187971 (create 0.000071, send 0.000237, recv 0.183873, ring 0.000632, delay 0.000000)
2f3d44d8b0ea:2943:2943 [0] NCCL INFO NCCL_IGNORE_DISABLED_P2P set by environment to 1.
2f3d44d8b0ea:2943:2943 [0] NCCL INFO ncclTopoGetCpuAffinity: Affinity for GPU 0 is empty, ignoring. (GPU affinity =  ; CPU affinity = 0-27).
2f3d44d8b0ea:2943:2943 [0] NCCL INFO comm 0x271b62c0 rank 0 nRanks 2 nNodes 2 localRanks 1 localRank 0 MNNVL 0
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Channel 00/02 : 0 1
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Channel 01/02 : 0 1
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Trees [0] 1/-1/-1->0->-1 [1] -1/-1/-1->0->1
2f3d44d8b0ea:2943:2943 [0] NCCL INFO P2P Chunksize set to 131072
2f3d44d8b0ea:2943:2943 [0] NCCL INFO PROFILER/Plugin: Could not find: libnccl-profiler.so
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Check P2P Type isAllDirectP2p 1 directMode 0 isAllCudaP2p 1
2f3d44d8b0ea:2943:2977 [0] NCCL INFO [Proxy Service] Device 0 CPU core 5
2f3d44d8b0ea:2943:2978 [0] NCCL INFO [Proxy Service UDS] Device 0 CPU core 24
2f3d44d8b0ea:2943:2943 [0] NCCL INFO TUNER/Plugin: Could not find: libnccl-tuner.so
2f3d44d8b0ea:2943:2943 [0] NCCL INFO threadThresholds 8/8/64 | 16/8/64 | 512 | 512
2f3d44d8b0ea:2943:2943 [0] NCCL INFO 2 coll channels, 2 collnet channels, 0 nvls channels, 2 p2p channels, 2 p2p channels per peer
2f3d44d8b0ea:2943:2943 [0] NCCL INFO CC Off, workFifoBytes 1048576
2f3d44d8b0ea:2943:2943 [0] NCCL INFO ncclCommInitRankConfig comm 0x271b62c0 rank 0 nranks 2 cudaDev 0 nvmlDev 0 busId 70 commId 0x77d5d096c29cef94 - Init COMPLETE
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Init timings - ncclCommInitRankConfig: rank 0 nranks 2 total 0.34 (kernels 0.13, alloc 0.00, bootstrap 0.19, allgathers 0.00, topo 0.01, graphs 0.00, connections 0.00, rest 0.00)
2f3d44d8b0ea:2943:2979 [0] NCCL INFO [Proxy Progress] Device 0 CPU core 10
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Channel 00/0 : 1[0] -> 0[0] [receive] via NET/Socket/0
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Channel 01/0 : 1[0] -> 0[0] [receive] via NET/Socket/0
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Channel 00/0 : 0[0] -> 1[0] [send] via NET/Socket/0
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Channel 01/0 : 0[0] -> 1[0] [send] via NET/Socket/0
2f3d44d8b0ea:2943:2943 [0] NCCL INFO Connected all rings, use ring PXN 0 GDR 0
[Rank 0] Starting training...
Step 0 | Loss: 1.4985
Step 10 | Loss: 1.1718
Step 20 | Loss: 1.5557
[Rank 0] Training complete.
2f3d44d8b0ea:2943:2943 [0] NCCL INFO comm 0x271b62c0 rank 0 nranks 2 cudaDev 0 busId 70 - Destroy COMPLETE
2f3d44d8b0ea:2943:2943 [0] NCCL INFO ENV/Plugin: Closing env plugin ncclEnvDefault
I0407 03:33:43.109000 2911 torch/distributed/elastic/agent/server/api.py:917] [default] worker group successfully finished. Waiting 300 seconds for other agents to finish.
I0407 03:33:43.109000 2911 torch/distributed/elastic/agent/server/api.py:970] Local worker group finished (WorkerState.SUCCEEDED). Waiting 300 seconds for other agents to finish
I0407 03:33:43.119000 2911 torch/distributed/elastic/agent/server/api.py:984] Done waiting for other agents. Elapsed: 0.00843501091003418 seconds
```

And this is from the second node.  It all worked!

```log
root@dd3bc9f2214d:/app# NCCL_DEBUG=INFO LOGLEVEL=DEBUG NCCL_SOCKET_IFNAME=wg0 STEPS=30 torchrun   --nnodes=2 --nproc_per_node=1 --node_rank=1   --rdzv_id=test_job --rdzv_backend=c10d --rdzv_endpoint=10.100.10.1:29500   --local_addr=10.100.10.2   /app/train.py
I0407 03:33:37.836000 490 torch/distributed/run.py:735] Using nproc_per_node=1.
I0407 03:33:37.916000 490 torch/distributed/launcher/api.py:131] Using default numa options = None
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224] Starting elastic_operator with launch configs:
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   entrypoint               : /app/train.py
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   min_nodes                : 2
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   max_nodes                : 2
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   nproc_per_node           : 1
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   run_id                   : test_job
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   rdzv_backend             : c10d
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   rdzv_endpoint            : 10.100.10.1:29500
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   rdzv_configs             : {'timeout': 900}
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   max_restarts             : 0
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   monitor_interval         : 0.1
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   log_dir                  : /tmp/torchelastic_667h1_sh
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   metrics_cfg              : {}
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   event_log_handler        : null
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   numa_options             : None
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   signals_to_handle        : SIGTERM,SIGINT,SIGHUP,SIGQUIT
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   duplicate_stdout_filters : []
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]   duplicate_stderr_filters : []
I0407 03:33:37.917000 490 torch/distributed/launcher/api.py:224]
I0407 03:33:37.947000 490 torch/distributed/elastic/agent/server/api.py:898] [default] starting workers for entrypoint: python3
I0407 03:33:37.948000 490 torch/distributed/elastic/agent/server/api.py:693] [default] Rendezvous'ing worker group
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539] [default] Rendezvous complete for workers. Result:
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   restart_count=0
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   master_addr=10.100.10.1
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   master_port=35121
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   group_rank=1
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   group_world_size=2
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   local_ranks=[0]
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   role_ranks=[1]
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   global_ranks=[1]
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   role_world_sizes=[2]
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   global_world_sizes=[2]
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]   event_log_handler=null
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:539]
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/api.py:701] [default] Starting worker group
I0407 03:33:39.097000 490 torch/distributed/elastic/agent/server/local_elastic_agent.py:299] use_agent_store: True
I0407 03:33:39.098000 490 torch/distributed/elastic/agent/server/local_elastic_agent.py:195] Environment variable 'TORCHELASTIC_ENABLE_FILE_TIMER' not found. Do not start FileTimerServer.
I0407 03:33:39.098000 490 torch/distributed/elastic/agent/server/local_elastic_agent.py:239] Environment variable 'TORCHELASTIC_HEALTH_CHECK_PORT' not found. Do not start health check.
dd3bc9f2214d:520:520 [0] NCCL INFO ENV/Plugin: Could not find: libnccl-env.so
dd3bc9f2214d:520:520 [0] NCCL INFO cudaDriverVersion 12080
dd3bc9f2214d:520:520 [0] NCCL INFO NCCL_SOCKET_IFNAME set by environment to wg0
dd3bc9f2214d:520:520 [0] NCCL INFO Bootstrap: Using wg0:10.100.10.2<0>
dd3bc9f2214d:520:520 [0] NCCL INFO NCCL version 2.28.9+cuda12.9
dd3bc9f2214d:520:520 [0] NCCL INFO Comm config Blocking set to 1
dd3bc9f2214d:520:520 [0] NCCL INFO NET/Plugin: Could not find: libnccl-net.so
dd3bc9f2214d:520:520 [0] NCCL INFO Failed to open libibverbs.so[.1]
dd3bc9f2214d:520:520 [0] NCCL INFO transport/net_ib.cc:852 -> 3
dd3bc9f2214d:520:520 [0] NCCL INFO Failed to initialize NET plugin IB
dd3bc9f2214d:520:520 [0] NCCL INFO NCCL_SOCKET_IFNAME set by environment to wg0
dd3bc9f2214d:520:520 [0] NCCL INFO NET/Socket : Using [0]wg0:10.100.10.2<0>
dd3bc9f2214d:520:520 [0] NCCL INFO Initialized NET plugin Socket
dd3bc9f2214d:520:520 [0] NCCL INFO Assigned NET plugin Socket to comm
dd3bc9f2214d:520:520 [0] NCCL INFO Using network Socket
dd3bc9f2214d:520:520 [0] NCCL INFO ncclCommInitRankConfig comm 0x26af6510 rank 1 nranks 2 cudaDev 0 nvmlDev 0 busId 70 commId 0x77d5d096c29cef94 - Init START
dd3bc9f2214d:520:520 [0] NCCL INFO RAS client listening socket at ::1<28028>
dd3bc9f2214d:520:520 [0] NCCL INFO Bootstrap timings total 0.007266 (create 0.000047, send 0.001406, recv 0.003567, ring 0.000768, delay 0.000001)
dd3bc9f2214d:520:520 [0] NCCL INFO NCCL_IGNORE_DISABLED_P2P set by environment to 1.
dd3bc9f2214d:520:520 [0] NCCL INFO ncclTopoGetCpuAffinity: Affinity for GPU 0 is empty, ignoring. (GPU affinity =  ; CPU affinity = 0-27).
dd3bc9f2214d:520:520 [0] NCCL INFO comm 0x26af6510 rank 1 nRanks 2 nNodes 2 localRanks 1 localRank 0 MNNVL 0
dd3bc9f2214d:520:520 [0] NCCL INFO Trees [0] -1/-1/-1->1->0 [1] 0/-1/-1->1->-1
dd3bc9f2214d:520:520 [0] NCCL INFO P2P Chunksize set to 131072
dd3bc9f2214d:520:520 [0] NCCL INFO PROFILER/Plugin: Could not find: libnccl-profiler.so
dd3bc9f2214d:520:520 [0] NCCL INFO Check P2P Type isAllDirectP2p 1 directMode 0 isAllCudaP2p 1
dd3bc9f2214d:520:553 [0] NCCL INFO [Proxy Service] Device 0 CPU core 17
dd3bc9f2214d:520:554 [0] NCCL INFO [Proxy Service UDS] Device 0 CPU core 11
dd3bc9f2214d:520:520 [0] NCCL INFO TUNER/Plugin: Could not find: libnccl-tuner.so
dd3bc9f2214d:520:520 [0] NCCL INFO threadThresholds 8/8/64 | 16/8/64 | 512 | 512
dd3bc9f2214d:520:520 [0] NCCL INFO 2 coll channels, 2 collnet channels, 0 nvls channels, 2 p2p channels, 2 p2p channels per peer
dd3bc9f2214d:520:520 [0] NCCL INFO ncclCommInitRankConfig comm 0x26af6510 rank 1 nranks 2 cudaDev 0 nvmlDev 0 busId 70 commId 0x77d5d096c29cef94 - Init COMPLETE
dd3bc9f2214d:520:520 [0] NCCL INFO Init timings - ncclCommInitRankConfig: rank 1 nranks 2 total 0.17 (kernels 0.15, alloc 0.00, bootstrap 0.01, allgathers 0.00, topo 0.01, graphs 0.00, connections 0.00, rest 0.00)
dd3bc9f2214d:520:555 [0] NCCL INFO [Proxy Progress] Device 0 CPU core 27
dd3bc9f2214d:520:520 [0] NCCL INFO Channel 00/0 : 0[0] -> 1[0] [receive] via NET/Socket/0
dd3bc9f2214d:520:520 [0] NCCL INFO Channel 01/0 : 0[0] -> 1[0] [receive] via NET/Socket/0
dd3bc9f2214d:520:520 [0] NCCL INFO Channel 00/0 : 1[0] -> 0[0] [send] via NET/Socket/0
dd3bc9f2214d:520:520 [0] NCCL INFO Channel 01/0 : 1[0] -> 0[0] [send] via NET/Socket/0
dd3bc9f2214d:520:520 [0] NCCL INFO Connected all rings, use ring PXN 0 GDR 0
[Rank 1] Starting training...
[Rank 1] Training complete.
dd3bc9f2214d:520:520 [0] NCCL INFO comm 0x26af6510 rank 1 nranks 2 cudaDev 0 busId 70 - Destroy COMPLETE
dd3bc9f2214d:520:520 [0] NCCL INFO ENV/Plugin: Closing env plugin ncclEnvDefault
I0407 03:33:43.118000 490 torch/distributed/elastic/agent/server/api.py:917] [default] worker group successfully finished. Waiting 300 seconds for other agents to finish.
I0407 03:33:43.118000 490 torch/distributed/elastic/agent/server/api.py:970] Local worker group finished (WorkerState.SUCCEEDED). Waiting 300 seconds for other agents to finish
I0407 03:33:43.120000 490 torch/distributed/elastic/agent/server/api.py:984] Done waiting for other agents. Elapsed: 0.0019180774688720703 seconds
```

### Conclusion

We just confirmed PyTorch DDP worked with a WireGuard network established between Docker containers.  There are special configurations needed:

* Specify `--rdzv_conf=is_host=1` for the master node because PyTorch doesn't see secondary IP addresses to check if the rendezvous endpoint is itself or not
* Specify `--local_addr` for every node to communicate via IP addresses instead of hostname

Actually specifying these parameters along with other standard DDP parameters such as `rdzv_endpoint` or `nnodes` is error-prone.  I created [a sample Docker image](https://github.com/kinesis-network/docker-image-samples/tree/main/11-torchrun) to semi-automated these configurations.

The question remains: Is this a bug in PyTorch we should report?

My answer is yes.  `--rdzv_conf=is_host=1` is a great workaround, but PyTorch should check all IP addresses assigned.  At the same time, I cannot find a clean solution for this yet.  It's strangely difficult to enumerate all IP addressese on Linux.  One solution AI suggested is to use [`fcntl.ioctl`](https://docs.python.org/3/library/fcntl.html#fcntl.ioctl) , but I believe it will be rejected because it looks too "C-style" or less compatible.  I'll think through it further.

Anyway, Kinesis now supports PyTorch DDP.  If you have a model to train, go to [https://portal.kinesis.network](https://portal.kinesis.network/) and run it!


# Why Compute Should Work Like Power on Tap

Imagine a world before the electricity grid. Every factory and household had to run its own generator — expensive, inefficient, and hard to scale. The breakthrough wasn’t just central power plants; it was the grid, the outlet, and the system that made electricity available everywhere, instantly. Compute is following the same trajectory. Today, organizations still buy, rent, and manage their own servers, or lock themselves into siloed cloud providers. At Kinesis, we believe the next leap is making compute work like electricity: seamless, reliable, and universally accessible. That’s the vision behind our electricity grid analogy.

## **The Electricity Grid Analogy**

#### **1. Generators → On-Prem Hardware**

In the early days, electricity was produced locally with generators. Every factory or household that wanted electricity had to generate its own.

💡 In compute, this is the same as enterprises buying servers and GPUs, running them in their own data centers, and putting them on the balance sheet.

***

#### **2. Power Plants → Cloud Providers**

Centralized power plants (coal, hydro, later nuclear) brought economies of scale, producing electricity more efficiently and selling it directly to customers.

💡 In compute, these are the hyperscalers (AWS, Azure, Google) and Tier 2 data centers. They run massive facilities, expose APIs, and sell compute capacity.

**Better economics than generators, but still siloed** — each provider has its own interfaces, contracts, and pricing models.

***

#### **3. The Grid (Wiring + Settlement) → Kinesis Protocol**

What really transformed electricity was the grid — the shared infrastructure that connected all power stations to all consumers.

The grid has two key elements:

* **Wiring (transmission lines):** the infrastructure that connects sources and destinations.
* **Settlement system/meters:** standardized measurement and billing, ensuring usage is recorded and suppliers are paid fairly.

💡In compute, this is the **Kinesis Protocol** (the Web3 layer):

* It connects all compute suppliers (hyperscalers, Tier 2 providers, crowdsourced GPUs, niche devices) to all customers.
* **Protocol + smart contracts = the meter and trust layer.** They measure usage, enforce performance guarantees, and handle settlement logic.
* **JOULE = the global compute currency.** Electricity grids are local and settle in local currencies; compute is inherently global. JOULE provides the first universal settlement unit — enabling per-second micropayments to millions of suppliers across borders.

***

#### **4. The Power Outlet → Kinesis Portal (IaaS)**

For consumers, the key innovation wasn’t the plant or the grid — it was the outlet. You just plug in, and electricity flows.

💡In compute, this is the **Kinesis Portal** (the IaaS interface):

* Customers upload workloads, set preferences, and simply “plug in.”
* All Web3 complexity (wallets, staking, tokens) is abstracted away.
* Enterprises pay in USD, suppliers can withdraw in USD — behind the scenes, every transaction is settled in JOULE.

***

#### **5. The Utility Company → Kinesis Network Inc. (KNI, Web2 Company)**

In electricity, your local utility manages your relationship with the grid. You pay them in your local currency, they ensure service continuity, and they handle wholesale settlement in the background.

💡In compute, this is **KNI (Kinesis Network Inc., the Web2 business):**

* Operates the customer-facing business.
* Sells compute via the Portal.
* Bills in USD, abstracts away protocol complexity, and guarantees SLAs.

***

#### **6. The Grid Operator → Kinesis Network Foundation (KNF, Web3 Company, governed by DAO)**

Electric grids are overseen by independent system operators (ISO) or regulators, responsible for long-term stability and expansion.

💡In compute, this is **KNF (Kinesis Network Foundation / DAO):**

* Owns and maintains the underlying protocol infrastructure.
* Issues and governs **JOULE**, the native currency of the protocol.
* Oversees the development, upgrade, and governance of the smart contracts that measure, enforce, and settle activity across the network.
* Its north star is to maximize the protocol’s long-term free cash flow and durability.
* Funds, supports, and accelerates ventures that increase protocol revenue.
* Over time, ensures **KNI is just one of many businesses plugged into the grid** — not the only one.

***

#### **7. The Appliances → Demand Side (Customers)**

Electricity became transformative when any device — from a light bulb to an entire factory — could plug into the same outlet and get exactly the power it needed. The outlet didn’t change, but what you could do with it scaled infinitely.

💡 In compute, this is the **demand side**:

* From **simple web apps and lightweight services** (like charging a phone or turning on a light bulb).
* To **AI inference workloads, ongoing enterprise applications, and smaller ML jobs** (like plugging in household appliances — steady, moderate power draw).
* To **large-scale AI training runs, scientific simulations, and industrial-grade HPC** (like running a steel mill, powering a subway system, or lighting up a skyscraper — massive continuous demand).

Customers all plug into the **same Kinesis Portal**. That single interface abstracts away the complexity of scale, delivering compute as flexibly as electricity itself — **a true utility, for every workload.**

***

The electricity grid turned power into a true utility — invisible, reliable, and available on demand. Compute is on the same path. By connecting fragmented supply, abstracting away complexity, and aligning incentives through a shared protocol, Kinesis makes compute flow as effortlessly as electricity from a wall outlet. Whether you’re running lightweight apps or training the largest AI models, the experience should be the same: plug in, and it just works. The future of compute isn’t about owning infrastructure — it’s about having compute on tap.


# Enable WireGuard on NVIDIA Jetson

By Toshihito Kikuchi

In [the earlier article](https://docs.kinesis.network/blog/another-day-of-debugging), I mentioned Dynamo supports ARM64 devices. At that time, I tested Amazon EC2 [T4g](https://aws.amazon.com/ec2/instance-types/t4/) Instances and [Raspberry Pi 5](https://www.raspberrypi.com/products/raspberry-pi-5/). As a third ARM64 device to test, I purchased [NVIDIA Jetson Orin Nano](https://developer.nvidia.com/embedded/learn/get-started-jetson-orin-nano-devkit).

After landing several patches, I was able to run Dynamo on Jetson. However, you need to do one extra step before installing Dynamo on your device: Build and install WireGuard on your end. Well, I could provide pre-built binaries, but I’d like to encourage people to build linux kernel by yourself. It’s fun. And Jetson as a Kinesis Node won’t be a mainstream scenario anyway.

I saw several forums discussing this topic too. Hopefully this article will help non-Kinesis users as well.

### Devices needed

I purchased:

1. NVIDIA Jetson Orin Nano Super Developer Kit ([Amazon](https://www.amazon.com/dp/B0BZJTQ5YP?ref=ppx_yo2ov_dt_b_fed_asin_title\&th=1))
2. Memory Card (I bought 64GB UHS-1 via [Amazon](https://www.amazon.com/dp/B0CYT352MW?ref=ppx_yo2ov_dt_b_fed_asin_title\&th=1). You pick any product.)

You also need a display supporting Display Port and a USB keyboard if you don’t have any.

### OS setup

Here's the official Getting Started Guide provided by NVIDIA:\
<https://developer.nvidia.com/embedded/learn/get-started-jetson-orin-nano-devkit>

In my case, the firmware was already up-to-date (36.4.x), so I directly downloaded SD Card Image of [JetPack 6.2.1](https://developer.nvidia.com/embedded/jetpack-sdk-621), but yours may not be. Don’t skip to check your firmware version. Please note that the latest version JetPack 7.0 is for Thor, not for Jetson Orin Nano (see <https://developer.nvidia.com/embedded/jetpack-archive>).

You can use your favorite writer to write an image to SD Card. I used [balena Etcher](https://etcher.balena.io/). Once it’s done, just insert your card and power on your Jetson. No drama here.

### Build WireGuard module

Let's double check WireGuard is not included in your device. You might be lucky to have it somehow. If it exists, it would be in `/lib/modules/$(uname -r)/kernel/drivers/net/wireguard`.

```
jetson@jetson:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.5 LTS
Release:        22.04
Codename:       jammy

jetson@jetson:~$ uname -a
Linux jetson 5.15.148-tegra #1 SMP PREEMPT Thu Sep 18 15:08:33 PDT 2025 aarch64 aarch64 aarch64 GNU/Linux

jetson@jetson:~$ ls -l /lib/modules/$(uname -r)/kernel/drivers/net
total 220
drwxr-xr-x  4 root root  4096 Oct  7 23:14 can
drwxr-xr-x  3 root root  4096 Jun 17 23:26 dsa
-rw-r--r--  1 root root 13265 Sep 18 15:40 dummy.ko
drwxr-xr-x 10 root root  4096 Jun 17 23:26 ethernet
drwxr-xr-x  2 root root  4096 Oct  7 23:14 ipa
-rw-r--r--  1 root root 39633 Sep 18 15:40 macvlan.ko
-rw-r--r--  1 root root 12177 Sep 18 15:40 macvtap.ko
drwxr-xr-x  2 root root  4096 Oct  7 23:14 mdio
-rw-r--r--  1 root root 10321 Sep 18 15:40 mdio.ko
drwxr-xr-x  2 root root  4096 Oct  7 23:14 pcs
drwxr-xr-x  2 root root  4096 Oct  7 23:14 phy
-rw-r--r--  1 root root 51305 Sep 18 15:40 tap.ko
drwxr-xr-x  2 root root  4096 Oct  7 23:14 usb
-rw-r--r--  1 root root 46089 Sep 18 15:40 veth.ko
drwxr-xr-x  2 root root  4096 Oct  7 23:14 vxlan
drwxr-xr-x  6 root root  4096 Oct  7 23:14 wireless
```

Since Linux 5.6, WireGuard is a [part](https://github.com/torvalds/linux/tree/master/drivers/net/wireguard) of kernel code in the mainstream, so you can get wireguard module by building linux kernel. Fortunately, NVIDIA provides [a guide](https://docs.nvidia.com/jetson/archives/r38.2/DeveloperGuide/SD/Kernel/KernelCustomization.html) on how to customize kernel.

The guide says we can download the kernel source with a script named “source\_sync.sh”. A weird thing is it doesn’t tell us where it is. It’s not included in the SD Card Image, JetPack. The answer is [here](https://forums.developer.nvidia.com/t/where-is-source-sync-sh/75149). It’s included in Driver Package. You can go to [Jetson Linux](https://developer.nvidia.com/embedded/jetson-linux) page and download "Driver Package (BSP)”. As of now, the package file is Jetson\_Linux\_r36.4.4\_aarch64.tbz2, which includes ./Linux\_for\_Tegra/source/source\_sync.sh.

To run source\_sync.sh, you need to specify “release-tag”, which the guide says is included in the release notes. But which release note should we check? AI tells me to do

```
jetson@jetson:/work$ head -n1 /etc/nv_tegra_release
# R36 (release), REVISION: 4.7, GCID: 42132812, BOARD: generic, EABI: aarch64, DATE: Thu Sep 18 22:54:44 UTC 2025
```

In the output above, the release is 36.4.7, but there is no corresponding release note. Given that the release note of [36.4.4](https://docs.nvidia.com/jetson/archives/r36.4.4/ReleaseNotes/Jetson_Linux_Release_Notes_r36.4.4.pdf) says its release tag is “jetson\_36.4.4”, we can make a guess. The source repo is <https://nv-tegra.nvidia.com/r/admin/repos/3rdparty/canonical/linux-jammy,tags>, so you can check the list of tags there.

```
jetson@jetson:/work/Linux_for_Tegra/source$ ./source_sync.sh -k -t jetson_36.4.7
Downloading default kernel/kernel-jammy-src source...
Cloning into '/work/Linux_for_Tegra/source/kernel/kernel-jammy-src'...
remote: Enumerating objects: 8617818, done.
remote: Counting objects: 100% (8617818/8617818), done.
remote: Compressing objects: 100% (1256098/1256098), done.
Receiving objects: 100% (8617818/8617818), 1.89 GiB | 14.65 MiB/s, done.
remote: Total 8617818 (delta 7322045), reused 8605598 (delta 7309852), pack-reused 0 (from 0)
Resolving deltas: 100% (7322045/7322045), done.
Checking objects: 100% (33554432/33554432), done.
The default kernel/kernel-jammy-src source is downloaded in: /work/Linux_for_Tegra/source/kernel/kernel-jammy-src
Syncing up with tag jetson_36.4.7...
Updating files: 100% (74252/74252), done.
Switched to a new branch 'mybranch_2025-10-08-1759908244'
/work/Linux_for_Tegra/source/kernel/kernel-jammy-src source sync'ed to tag jetson_36.4.7 successfully!
```

When we build linux kernel, we usually generate .config to customize build options with `menuconfig` or other tools. For Jetson Linux, however, the guide suggests to use `make -C kernel` to build, where Makefile applies [defconfig](https://nv-tegra.nvidia.com/r/plugins/gitiles/3rdparty/canonical/linux-jammy/+/refs/tags/jetson_36.4.7/arch/arm64/configs/defconfig) to build by default. Even if we run `menuconfig`, Makefile overrides .config with defconfig.

As you can see, `CONFIG_WIREGUARD` is not defined there.

```bash
jetson@jetson:/work/Linux_for_Tegra/source$ grep WIRE kernel/kernel-jammy-src/arch/arm64/configs/defconfig
CONFIG_SOUNDWIRE=m
CONFIG_SOUNDWIRE_QCOM=m
```

Let’s simply add `CONFIG_WIREGUARD=m` at the end of the file and kick off build. Once build is done, wireguard.ko is generated.

```bash
jetson@jetson:/work/Linux_for_Tegra/source$ tail -5 kernel/kernel-jammy-src/arch/arm64/configs/defconfig
# CONFIG_FUNCTION_GRAPH_TRACER is not set
# CONFIG_DYNAMIC_FTRACE is not set
CONFIG_MEMTEST=y

CONFIG_WIREGUARD=m

# Install prereq packages
jetson@jetson:/work/Linux_for_Tegra/source$ sudo apt install -y build-essential bc flex bison libssl-dev zstd libncurses
-dev

# Build! (Makefile specifies -j option accordingly)
jetson@jetson:/work/Linux_for_Tegra/source$ make -C kernel
...

jetson@jetson:/work/Linux_for_Tegra/source$ find . -name wireguard.ko
./kernel/kernel-jammy-src/drivers/net/wireguard/wireguard.ko
```

### Install WireGuard module

To install a single module, you simply copy a file under /lib/modules, generate dependencies, and load with `modprobe`.

```sh
sudo mkdir /lib/modules/$(uname -r)/kernel/drivers/net/wireguard/
sudo cp kernel/kernel-jammy-src/drivers/net/wireguard/wireguard.ko \
  /lib/modules/$(uname -r)/kernel/drivers/net/wireguard/
sudo depmod -a
sudo modprobe wireguard
```

Oops, the last `modprobe` command failed with the following error.

```bash
jetson@jetson:/work/Linux_for_Tegra/source$ sudo modprobe wireguard
modprobe: ERROR: could not insert 'wireguard': Unknown symbol in module, or unknown parameter (see dmesg)
```

Don’t panic. You can see more details with `dmesg`.

```bash
jetson@jetson:~$ sudo dmesg | tail
...
[10962.061383] wireguard: Unknown symbol chacha20poly1305_encrypt_sg_inplace (err -2)
[10962.061509] wireguard: Unknown symbol chacha20poly1305_encrypt (err -2)
[10962.061715] wireguard: Unknown symbol chacha20poly1305_decrypt_sg_inplace (err -2)
[10962.061798] wireguard: Unknown symbol xchacha20poly1305_encrypt (err -2)
[10962.061820] wireguard: Unknown symbol xchacha20poly1305_decrypt (err -2)
[10962.061913] wireguard: Unknown symbol chacha20poly1305_decrypt (err -2)
```

These are simple dependency errors. Searching code, you can easily find these symbols are implemented in libchacha20poly1305.ko, which is not installed in the kernel.

```
jetson@jetson:/work/Linux_for_Tegra/source/kernel/kernel-jammy-src/lib/crypto$ grep -nr chacha20poly1305_encrypt_sg_inplace
grep: libchacha20poly1305.o: binary file matches
chacha20poly1305-selftest.c:8928:		ret = chacha20poly1305_encrypt_sg_inplace(sg_src,
chacha20poly1305-selftest.c:9043:				if (!chacha20poly1305_encrypt_sg_inplace(sg_src,
grep: libchacha20poly1305.ko: binary file matches
grep: chacha20poly1305.o: binary file matches
chacha20poly1305.c:333:bool chacha20poly1305_encrypt_sg_inplace(struct scatterlist *src, size_t src_len,
chacha20poly1305.c:341:EXPORT_SYMBOL(chacha20poly1305_encrypt_sg_inplace);

jetson@jetson:/work/Linux_for_Tegra/source$ nm -g kernel/kernel-jammy-src/lib/crypto/libchacha20poly1305.ko | grep chacha20poly1305_encrypt_sg_inplace
0000000000000bd0 T chacha20poly1305_encrypt_sg_inplace
0000000037b34b92 A __crc_chacha20poly1305_encrypt_sg_inplace

jetson@jetson:/work/Linux_for_Tegra/source$ find /sys/module/$(uname -r) -name libchacha*
find: ‘/sys/module/5.15.148-tegra’: No such file or directory
```

Let’s install this module and try to load wireguard again.

```bash
sudo cp ./kernel/kernel-jammy-src/lib/crypto/libchacha20poly1305.ko \
  /lib/modules/$(uname -r)/kernel/lib/crypto/
sudo depmod -a
sudo modprobe wireguard
```

You’ll get the same error from `modprobe`, but it’s caused by different symbols.

```bash
[11355.067989] libchacha20poly1305: Unknown symbol poly1305_final_arch (err -2)
[11355.068039] libchacha20poly1305: Unknown symbol poly1305_init_arch (err -2)
[11355.068089] libchacha20poly1305: Unknown symbol poly1305_update_arch (err -2)
```

Repeat the same thing, searching code, identifying a missing module implementing the symbols. This time it’s poly1305-neon.ko. Neon is SIMD extension for ARM.

```bash
jetson@jetson:/work/Linux_for_Tegra/source/kernel/kernel-jammy-src/arch/arm64/crypto$ grep -nr poly1305_final_arch
grep: poly1305-neon.o: binary file matches
grep: poly1305-neon.ko: binary file matches
grep: poly1305-glue.o: binary file matches
poly1305-glue.c:170:void poly1305_final_arch(struct poly1305_desc_ctx *dctx, u8 *dst)
poly1305-glue.c:182:EXPORT_SYMBOL(poly1305_final_arch);
poly1305-glue.c:191:	poly1305_final_arch(dctx, dst);

jetson@jetson:/work/Linux_for_Tegra/source$ nm -g kernel/kernel-jammy-src/arch/arm64/crypto/poly1305-neon.ko | grep poly1305_final_arch
00000000f39f5240 A __crc_poly1305_final_arch
0000000000000e00 T poly1305_final_arch
```

Let’s just copy and try to load wireguard.ko once again.

```bash
sudo cp ./kernel/kernel-jammy-src/arch/arm64/crypto/poly1305-neon.ko \
  /lib/modules/$(uname -r)/kernel/arch/arm64/crypto/
sudo depmod -a
sudo modprobe wireguard
```

It should work this time, and you’ll see it’s loaded as below.

```bash
jetson@jetson:/work/Linux_for_Tegra/source$ lsmod | grep wire
wireguard              77824  0
libchacha20poly1305    16384  1 wireguard
ip6_udp_tunnel         20480  1 wireguard
udp_tunnel             24576  1 wireguard
libcurve25519_generic    36864  1 wireguard
ipv6                  471040  94 bridge,wireguard
```

### Verification

Dynamo uses wireguard through [our gateway container](https://hub.docker.com/r/kinesisorg/gateway-container). You can simulate this scenario by creating a minimum wireguard conf and running a container as follows. An endpoint can be random which doesn’t need to be valid for this verification purpose.

```bash
jetson@jetson:/work$ cat <<EOF > ${PWD}/wg-test.conf
[Interface]
PrivateKey = 6KvR4DZ1+uZom3S4VOBlbMtDpuZGGnbNEIFJfD4cjUg=
Address = 10.200.100.1/24
[Peer]
PublicKey = xFpe/+vmrUzQwtqpvVLB4AXygknLQo3f/qRR0AFXflQ=
AllowedIPs = 10.200.100.2/32
Endpoint = 1.2.3.4:51820
PersistentKeepalive = 25
EOF
jetson@jetson:/work$ docker run -d \
  --cap-add=NET_ADMIN \
  --device /dev/net/tun \
  --sysctl net.ipv4.ip_forward=1 \
  -v ${PWD}/wg-test.conf:/etc/wireguard/wg0.conf:ro \
  kinesisorg/gateway-container
250d35ac39c902515501ef863cf0d4cb91a2b407b75b6feab2fd6a41dc686c91
jetson@jetson:/work$ docker exec -it $(docker ps -q -f ancestor=kinesisorg/gateway-container) wg show
interface: wg0
  public key: i0JM5N5EVUDJ/08h5N4mt6bohSPrtXJZcNFqrsOULzA=
  private key: (hidden)
  listening port: 49594

peer: xFpe/+vmrUzQwtqpvVLB4AXygknLQo3f/qRR0AFXflQ=
  endpoint: 1.2.3.4:51820
  allowed ips: 10.200.100.2/32
  transfer: 0 B received, 592 B sent
  persistent keepalive: every 25 seconds
```

### Conclusion

Congratulations! Your Jetson device is ready to run Dynamo and join the Kinesis Network as a Global Node. In short, you need to clone Jetson Linux Kernel from NVIDIA’s repo, build it, and copy wireguard.ko along with a couple of dependent (crypto-related) modules. Pretty straightforward.

You might be interested in installing the kernel itself and debugging it. That’s good ambition. Let’s discuss it as another topic!


# Reaching out to home computers

By Toshihito Kikuchi

Kinesis Network transforms underutilized computing resources into scalable on-demand compute services. It’s expected most of them are home computers or on-premise corporate servers that don’t have public IP addresses. To achieve our goal, it’s essential to make these computers accessible from the Internet, but at the same time, we need to provide an unified, automated way to join the network. Asking owners of those devices to modify router settings or other network settings is not our option. In this article, I’m going to briefly explain how we deal with computers without having public IP addresses. By the way, we call these computers “Global Node”, in contract to “Datacenter Node”, to which a public IP address is assigned.

Whatever approach we take, we need to set up a globally accessible server that works as a proxy to connect to global nodes. In the earlier version of our product, we chose SSH and implemented a custom channel to forward each TCP connection. It worked well for a while, but we discontinued this approach because of scalability concern and high maintenance cost.

The next technology we chose is [WireGuard](https://www.wireguard.com/). We spotlighted this technology to solve another networking challenge, and then we realized this can be a replacement of SSH to achieve Global Nodes. WireGuard is basically a VPN. The basic idea is to install WireGuard on both the proxy side and the global node side and to redirect traffic to the proxy to the node through WireGuard network. In this setup, we call this proxy role “AppProxy”, in contrast to “NodeProxy”, which is to proxy a node’s admin channels (explained later).

A picture is worth a thousand words. Here’s the entire topology of a global node hosting an App with a single port.

<figure><img src="https://4256139742-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fobv1L4EfK71xeiC2NpU7%2Fuploads%2FjVQLdWg6KoORTGHoCcSj%2Fimage.png?alt=media&amp;token=bcef5736-6244-4b9f-a3d3-8738f4a5e355" alt=""><figcaption></figcaption></figure>

In this diagram, you can see two outer boxes, Global Node and AppProxy. Both of them are Kinesis nodes, where Dynamo run and manage Kinesis Apps. Traffic comes from the right side. A node accepts two types of traffic: one is for normal app traffic like HTTP for Nginx, and the other is admin channels that host live docker log streams via gRPC and exec sessions via WebSocket. Admin channels are usually consumed by the Portal. An AppProxy opens accessible ports for these services, but on top of them, we have Load Balancer for App ports and NodeProxy for admin channels.

To establish a WireGuard network, we need to install WireGuard on both GlobalNode and AppProxy. Instead of directly installing WireGuard on these hosts, we decided to “kinesify” WireGuard and installed it as a Kinesis app \[1]. This design drastically simplifies our landscape because we can reuse most of code to set up WireGuard. We call a WireGuard on AppProxy “Frontend Gateway”, and the one on a Global Node “Backend Gateway”. As the diagram shows, we use 10.200.100.1 and 10.200.100.2 for all WireGuard networks. Using the same subnet for everything is to simplify our code. There is no specific reason why I chose this subnet. Maybe I should have chosen a more exotic subnet like 31.41.59.0/24. For docker bridge network, Dynamo selects an unused one among 192.168.0.0/24, 192.168.1.0/24, etc. This means a global node cannot run more than 255 apps (and one for admin channels). I think it’s a reasonable limit, but we can increase it by changing the network bits though it’s hardcoded.

Once a WireGuard network is established, a frontend gateway can access to a backend gateway via the WireGuard network’s IP even though the Global Node doesn’t have a global IP address.

The next step is to redirect traffic to AppProxy to the Global Node. We simply configure DNAT to redirect all traffic to AppProxy to the Global Node’s address on the WireGuard network. Here are the actual commands on AppProxy. To do this, we leverage a feature in Kinesis App to plant arbitrary files on a node and execute arbitrary commands on startup.

```
iptables -t nat -A PREROUTING -p tcp -j DNAT --to-destination 10.200.100.2
iptables -A FORWARD -p tcp -d 10.200.100.2 -j ACCEPT
iptables -t nat -A POSTROUTING -p tcp -d 10.200.100.2 -j MASQUERADE
```

For App ports, we configure one more DNAT to redirect traffic to the Global Node to the actual user app which is on the same Docker Bridge network. We create a custom network per App, so traffic to each App is completely separated on a global node.

Since admin channels are hosted by Dynamo Admin Server which is running on host, we cannot use DNAT unless we make host network accessible from a docker container, which is not recommended because of security concern. Therefore, we decided to include SSH in the gateway app and get Dynamo Admin Server to listen admin channels inside a backend gateway via [SSH remote port forwarding](https://en.wikipedia.org/wiki/Port_forwarding#Remote_port_forwarding).

That’s the overview of our global nodes. How does the network set up this whole thing? Here are the flow to establish admin channels when a global node joins the network:

1. Backend selects an AppProxy node to pair with
2. Backend installs a frontend gateway app on the AppProxy
3. Dynamo fetches dynamically assigned ports (WG’s listener, admin channels) and sends them back to the backend
4. Backend registers exposed admin channels with NodeProxy
5. Backend installs a backend gateway app on the global node with the config to connect to the AppProxy
6. Throughout the flow, Dynamo Admin Server keeps asking Dynamo for an SSH endpoint. Once it’s available, it automatically connects and listens ports inside a backend gateway.

When the network receives a request to run an App on a global node, it repeats almost the same steps to install frontend/backend gateway on both nodes.

\[1] Kinesified Gateway App: <https://github.com/kinesis-network/gateway-container>


# Another day of debugging

By Toshihito Kikuchi

One of the key components in the Kinesis ecosystem is Dynamo, an agent program to promote a computer to a computing node in the network. Recently, I made a patch to enable Dynamo to run on ARM64 devices. Usually we use Ubuntu as Linux distro for everyday use. On this day, however, I was in a mood to try something new and chose Amazon Linux in AWS. The story began there.

### Dynamo got stuck

On a node, we run Dynamo as a systemd service. When I started the Dynamo service on an Amazon Linux node, the process started, but I realized some important events were not logged. It seemed that the process was stuck in the middle of the startup phase.

The first triage is to run the exact same command manually, and when I ran the command on shell, it just worked normally. Here are the output: the former is from systemd; the latter is from manual run.

```jsx
#### systemd

[ec2-user@ip-172-31-15-164 kinesis-dynamo]$ sudo systemctl start dynamo
[ec2-user@ip-172-31-15-164 kinesis-dynamo]$ sudo journalctl -u dynamo -f
Aug 27 23:39:20 ip-172-31-15-164.ec2.internal systemd[1]: dynamo.service: Deactivated successfully.
Aug 27 23:39:20 ip-172-31-15-164.ec2.internal systemd[1]: Stopped dynamo.service - "Dynamo node service".
Aug 27 23:39:20 ip-172-31-15-164.ec2.internal systemd[1]: dynamo.service: Consumed 42.170s CPU time.
Aug 27 23:39:27 ip-172-31-15-164.ec2.internal systemd[1]: Started dynamo.service - "Dynamo node service".
Aug 27 23:39:27 ip-172-31-15-164.ec2.internal dynamo[37562]: time=2025-08-27T23:39:27.132Z level=INFO msg="Loaded your wallet" addr=0xa5e07b0a3944dd9158a8a72a6794004201669468 file=/opt/dynamo/id_ecdsa
Aug 27 23:39:27 ip-172-31-15-164.ec2.internal dynamo[37562]: time=2025-08-27T23:39:27.132Z level=INFO msg="Loaded AppCacheFile" file=/opt/dynamo/app-cache.json
Aug 27 23:39:27 ip-172-31-15-164.ec2.internal dynamo[37562]: time=2025-08-27T23:39:27.132Z level=INFO msg="Loaded a valid certificate" file=/opt/dynamo/backend.crt
Aug 27 23:39:27 ip-172-31-15-164.ec2.internal dynamo[37562]: time=2025-08-27T23:39:27.132Z level=INFO msg="Loaded config" file=/opt/dynamo/config.json
Aug 27 23:39:27 ip-172-31-15-164.ec2.internal dynamo[37562]: time=2025-08-27T23:39:27.182Z level=ERROR msg="Cannot initialize Docker Manager" err="Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?"
Aug 27 23:39:27 ip-172-31-15-164.ec2.internal dynamo[37562]: time=2025-08-27T23:39:27.183Z level=INFO msg="Serving gRPC" laddr=/tmp/kinesis-dynamo.sock
^C
[ec2-user@ip-172-31-15-164 kinesis-dynamo]$ ps -ef | grep node
root         778       2  0 22:38 ?        00:00:00 [xfs-inodegc/nvm]
ec2-user   37562       1  1 23:39 ?        00:00:00 /opt/dynamo/noded -config=/opt/dynamo/config.json
ec2-user   37577   36391  0 23:39 pts/0    00:00:00 grep --color=auto node

### manual run

[ec2-user@ip-172-31-15-164 kinesis-dynamo]$ /opt/dynamo/noded -config=/opt/dynamo/config.json
time=2025-08-27T23:40:03.620Z level=INFO msg="Loaded your wallet" addr=0xa5e07b0a3944dd9158a8a72a6794004201669468 file=/opt/dynamo/id_ecdsa
time=2025-08-27T23:40:03.620Z level=INFO msg="Loaded AppCacheFile" file=/opt/dynamo/app-cache.json
time=2025-08-27T23:40:03.621Z level=INFO msg="Loaded a valid certificate" file=/opt/dynamo/backend.crt
time=2025-08-27T23:40:03.621Z level=INFO msg="Loaded config" file=/opt/dynamo/config.json
time=2025-08-27T23:40:03.660Z level=ERROR msg="Cannot initialize Docker Manager" err="Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?"
time=2025-08-27T23:40:03.661Z level=INFO msg="Serving gRPC" laddr=/tmp/kinesis-dynamo.sock

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
WARNING:

You should always run with libnvidia-ml.so that is installed with your
NVIDIA Display Driver. By default it's installed in /usr/lib and /usr/lib64.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
time=2025-08-27T23:40:03.767Z level=ERROR msg="Cannot initialize NVML" result=9
time=2025-08-27T23:40:03.802Z level=INFO msg="Successfully registered with Node Manager" address=0xa5e07b0a3944dd9158a8a72a6794004201669468 ip=""
^C
```

As you can see in the second output, we use [NVML](https://developer.nvidia.com/management-library-nvml) to collect information about NVIDIA GPU. The warning and the error of "Cannot initialize NVML” in the output are expected because this node doesn’t have any GPU. The problem is these warning and error were not logged from Dynamo running as a systemd service.

### Debugging with Delve

Since Dynamo is written in Go, the second triage is to attach [Delve](https://github.com/go-delve/delve) to a systemd process to debug. I could debug in interactive mode, but if the target process is a daemon like this case, I always start with batch mode i.e. running dlv to print all goroutines and dump it to a file. Why? If you pause the process for a long time, systemd may trigger restart. In general, you should keep your debug target fresh and untouched, like cooking fish. Dumping to a file also helps if the output is too long. You can use your favorite editor to analyze the output for unlimited time. Here are the commands to dump goroutines to a file.

```jsx
[ec2-user@ip-172-31-15-164 ~]$ sudo systemctl start dynamo
[ec2-user@ip-172-31-15-164 ~]$ ps -ef | grep noded
ec2-user   38720       1  3 23:54 ?        00:00:00 /opt/dynamo/noded -config=/opt/dynamo/config.json
ec2-user   38735   36391  0 23:54 pts/0    00:00:00 grep --color=auto noded
[ec2-user@ip-172-31-15-164 ~]$ dlv --allow-non-terminal-interactive attach 38720 <<< "goroutines -t 100" >goroutines.log
[ec2-user@ip-172-31-15-164 ~]$ ls -lh
total 64K
drwxr-xr-x.  4 ec2-user ec2-user  28 Aug 27 22:49 go
-rw-r--r--.  1 ec2-user ec2-user 45K Aug 27 23:54 goroutines.log
drwxr-xr-x. 22 ec2-user ec2-user 16K Aug 27 22:49 kinesis-dynamo
```

I found a groutine that looks indeed stuck.

```jsx
* Goroutine 36 - User: _cgo_gotypes.go:150 github.com/kinesis-network/kinesis-dynamo/nvml._Cfunc_NvmlInit (0x8fb5f0) (thread 38720) [select]
	 0  0x0000ffff87f68ff8 in [1m???[0m
	     at [1m?:-1[0m
	 1  0x000000000048de18 in [1mruntime.systemstack_switch[0m
	     at ./.go/src/runtime/[1masm_arm64.s:249[0m
	 2  0x000000000048572c in [1mruntime.cgocall[0m
	     at ./.go/src/runtime/[1mcgocall.go:185[0m
	 3  0x00000000008fb5f0 in [1mgithub.com/kinesis-network/kinesis-dynamo/nvml._Cfunc_NvmlInit[0m
	     at [1m_cgo_gotypes.go:150[0m
	 4  0x00000000008fb708 in [1mgithub.com/kinesis-network/kinesis-dynamo/nvml.Init.func1[0m
	     at ./kinesis-dynamo/nvml/[1mnvml.go:59[0m
	 5  0x00000000004998e0 in [1msync.(*Once).doSlow[0m
	     at ./.go/src/sync/[1monce.go:78[0m
	 6  0x00000000008fb6b4 in [1msync.(*Once).Do[0m
	     at ./.go/src/sync/[1monce.go:69[0m
	 7  0x00000000008fb6b4 in [1mgithub.com/kinesis-network/kinesis-dynamo/nvml.Init[0m
	     at ./kinesis-dynamo/nvml/[1mnvml.go:58[0m
	 8  0x00000000009bca08 in [1mgithub.com/kinesis-network/kinesis-dynamo/pulse.FillGpuInfo[0m
	     at ./kinesis-dynamo/pulse/[1mgpu.go:14[0m
	 9  0x0000000000d25f1c in [1mgithub.com/kinesis-network/kinesis-dynamo/core.(*Server).collectNodeInfo[0m
	     at ./kinesis-dynamo/core/[1mserver.go:389[0m
	10  0x0000000000d25720 in [1mgithub.com/kinesis-network/kinesis-dynamo/core.(*Server).advertiseSelf[0m
	     at ./kinesis-dynamo/core/[1mserver.go:340[0m
	11  0x0000000000d26c8c in [1mgithub.com/kinesis-network/kinesis-dynamo/core.(*Server).Serve.func1[0m
	     at ./kinesis-dynamo/core/[1mserver.go:464[0m
	12  0x0000000000490334 in [1mruntime.goexit[0m
	     at ./.go/src/runtime/[1masm_arm64.s:1268[0m
```

As mentioned above, Dynamo uses NVML. Since NVML is a C library (and Python bindings are available too), Dynamo implements a stub written in C to consume NVML APIs and compile/link it with [CGO](https://golang.google.cn/cmd/cgo/). The callstack above indicates that stub code never returns. Below is the actual Go code of Dynamo. The frame #7 in the output above indicates the call to `C.NvmlInit()` below never returns. Well, there is nothing interesting here in Go code.

```go
func Init(logger utils.ILogger) bool {
	nvmlInitOnce.Do(func() {
		if result := C.NvmlInit(); result == 0 {
			nvmlInitialized = true
			logger.Info("Initialized NVML")
		} else {
			logger.Error("Cannot initialize NVML", "result", result)
		}
	})
	return nvmlInitialized
}
```

What does `NvmlInit()` do? You may be surprised. Here’s the actual C code of Dynamo.

```c
nvmlReturn_t NvmlInit() {
  return nvmlInit();
}
```

It does nothing but calling a NVML function `nvmlInit` , which is actually an alias of a versioned function `nvmlInit_v2` . [NVIDIA’s reference](https://docs.nvidia.com/deploy/archive/R535/nvml-api/group__nvmlInitializationAndCleanup.html) has some description and notes about this method, but no information mentions a possible “stuck”.

So what’s next? NVML is closed-source unfortunately. Calling NVIDIA support?

### Debugging with GDB

Of course not. We dig deeper with [GDB](https://sourceware.org/gdb/) (Or you could go with [LLDB](https://lldb.llvm.org/) if you prefer). No source code is no problem.

Let’s restart dynamo and attach gdb to the process.

```c
[ec2-user@ip-172-31-15-164 ~]$ sudo systemctl restart dynamo
[ec2-user@ip-172-31-15-164 ~]$ ps -ef | grep noded
ec2-user   42723       1  2 01:52 ?        00:00:00 /opt/dynamo/noded -config=/opt/dynamo/config.json
ec2-user   42787   36391  0 01:52 pts/0    00:00:00 grep --color=auto noded
[ec2-user@ip-172-31-15-164 ~]$ gdb -q /opt/dynamo/noded
Reading symbols from /opt/dynamo/noded...
warning: File "/home/ec2-user/.go/.versions/1.25.0/src/runtime/runtime-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /home/ec2-user/.go/.versions/1.25.0/src/runtime/runtime-gdb.py
line to your configuration file "/home/ec2-user/.config/gdb/gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/home/ec2-user/.config/gdb/gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
(gdb) attach 42723
Attaching to program: /opt/dynamo/noded, process 42723
[New LWP 42788]
[New LWP 42773]
[New LWP 42759]
[New LWP 42739]
[New LWP 42729]
[New LWP 42728]
[New LWP 42727]
[New LWP 42726]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
runtime.futex () at /home/ec2-user/.go/src/runtime/sys_linux_arm64.s:651
651             SVC
Missing rpms, try: dnf --enablerepo='*debug*' install glibc-debuginfo-2.34-196.amzn2023.0.1.aarch64
```

If you want to dump callstacks of all threads as we did with dlv earlier, you can run

```c
(gdb) thread apply all bt
```

However, let’s try another approach this time. First, list all threads.

```c
(gdb) info threads
  Id   Target Id                                 Frame
* 1    Thread 0xffffbc4f5020 (LWP 42723) "noded" runtime.futex ()
    at /home/ec2-user/.go/src/runtime/sys_linux_arm64.s:651
  2    Thread 0xffff70f3e100 (LWP 42788) "noded" runtime.futex ()
    at /home/ec2-user/.go/src/runtime/sys_linux_arm64.s:651
  3    Thread 0xffff7194e100 (LWP 42773) "noded" runtime.futex ()
    at /home/ec2-user/.go/src/runtime/sys_linux_arm64.s:651
  4    Thread 0xffff7235e100 (LWP 42759) "noded" internal/runtime/syscall.Syscall6 ()
    at /home/ec2-user/.go/src/internal/runtime/syscall/asm_linux_arm64.s:17
  5    Thread 0xffff72e2e100 (LWP 42739) "noded" runtime.futex ()
    at /home/ec2-user/.go/src/runtime/sys_linux_arm64.s:651
  6    Thread 0xffff7395e100 (LWP 42729) "noded" runtime.futex ()
    at /home/ec2-user/.go/src/runtime/sys_linux_arm64.s:651
  7    Thread 0xffff743ae100 (LWP 42728) "noded" 0x0000ffffbc38aff8 in read () from /lib64/libc.so.6
  8    Thread 0xffff74dbe100 (LWP 42727) "noded" runtime.futex ()
    at /home/ec2-user/.go/src/runtime/sys_linux_arm64.s:651
  9    Thread 0xffff7580e100 (LWP 42726) "noded" runtime.futex ()
    at /home/ec2-user/.go/src/runtime/sys_linux_arm64.s:651
```

Probably we can ignore threads with `runtime.futex` because many are waiting there so it looks like Go’s management stuff. This means we’re interested in thread #4 or #7. Let’s check them one by one. In this case, thread #4 wasn’t interesting and #7 was what we were looking for.

```c
(gdb) thread 7
[Switching to thread 7 (Thread 0xffff62976100 (LWP 40398))]
#0  0x0000ffffaa952ff8 in read () from /lib64/libc.so.6
(gdb) bt
#0  0x0000ffffaa952ff8 in read () from /lib64/libc.so.6
#1  0x0000ffffaa8ed888 in __GI__IO_file_underflow () from /lib64/libc.so.6
#2  0x0000ffffaa8ee944 in _IO_default_uflow () from /lib64/libc.so.6
#3  0x0000ffffaa8e0ddc in _IO_getline_info () from /lib64/libc.so.6
#4  0x0000ffffaa8dfb20 in fgets () from /lib64/libc.so.6
#5  0x0000000000d824a4 in ?? ()
#6  0x0000000000d982c0 in nvmlInit_v2 ()
#7  0x0000000000d75c3c in NvmlInit () at nvml.c:87
#8  0x0000000000d757f0 in _cgo_4e2c63ea9b15_Cfunc_NvmlInit (v=0x40002259a8) at /tmp/go-build/cgo-gcc-prolog:114
#9  0x000000000049013c in runtime.asmcgocall () at /home/ec2-user/.go/src/runtime/asm_arm64.s:1049
#10 0x0000004000003dc0 in ?? ()
Backtrace stopped: not enough registers or memory available to unwind further
```

It’s in the middle of our stub function `NvmlInit` , which is calling `fgets` . Let’s confirm if `fgets` really never returns. This is an important step because this stuck could be caused by an infinite loop calling `fgets` repeatedly.

```c
(gdb) b *0xd824a4
Breakpoint 1 at 0xd824a4
(gdb) c
Continuing.
```

I left it for seconds and this breakpoint wasn’t hit. So Dynamo was stuck because `fgets` never returned. What kind of data are we reading from what? Standard input?

We’ll get there, but before that, let’s step back a little bit and remember this happens only when we run Dynamo via systemd. Two possibilities. If we run Dynamo directly from shell, `fgets` just returns without any problem, or `fgets` isn’t called at all. Let’s find it out. I took this approach because debugging a normal process is much easier than debugging a systemd process. We don’t have to check the PID, and we don’t have to worry about automatic restart, etc.

I just ran dynamo with gdb and set a breakpoint at `nvmlInit_v2`.

```c
[ec2-user@ip-172-31-15-164 ~]$ gdb -q /opt/dynamo/noded
Reading symbols from /opt/dynamo/noded...
warning: File "/home/ec2-user/.go/.versions/1.25.0/src/runtime/runtime-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /home/ec2-user/.go/.versions/1.25.0/src/runtime/runtime-gdb.py
line to your configuration file "/home/ec2-user/.config/gdb/gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/home/ec2-user/.config/gdb/gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
(gdb) b nvmlInit_v2
Breakpoint 1 at 0xd98264
(gdb) r -config=/opt/dynamo/config.json
Starting program: /opt/dynamo/noded -config=/opt/dynamo/config.json
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[New Thread 0xffffb130a100 (LWP 43272)]
[New Thread 0xffffabfff100 (LWP 43273)]
[New Thread 0xffffab5ef100 (LWP 43274)]
[New Thread 0xffffaabdf100 (LWP 43275)]
[New Thread 0xffffaa1cf100 (LWP 43276)]
time=2025-08-28T02:09:22.824Z level=INFO msg="Loaded your wallet" addr=0xa5e07b0a3944dd9158a8a72a6794004201669468 file=/opt/dynamo/id_ecdsa
time=2025-08-28T02:09:22.825Z level=INFO msg="Loaded AppCacheFile" file=/opt/dynamo/app-cache.json
time=2025-08-28T02:09:22.825Z level=INFO msg="Loaded a valid certificate" file=/opt/dynamo/backend.crt
time=2025-08-28T02:09:22.825Z level=INFO msg="Loaded config" file=/opt/dynamo/config.json
[New Thread 0xffffa97bf100 (LWP 43277)]
time=2025-08-28T02:09:22.873Z level=ERROR msg="Cannot initialize Docker Manager" err="Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?"
[New Thread 0xffffa8daf100 (LWP 43278)]
time=2025-08-28T02:09:22.874Z level=INFO msg="Serving gRPC" laddr=/tmp/kinesis-dynamo.sock
[Switching to Thread 0xffffabfff100 (LWP 43273)]

Thread 3 "noded" hit Breakpoint 1, 0x0000000000d98264 in nvmlInit_v2 ()
Missing rpms, try: dnf --enablerepo='*debug*' install glibc-debuginfo-2.34-196.amzn2023.0.1.aarch64
(gdb) bt
#0  0x0000000000d98264 in nvmlInit_v2 ()
#1  0x0000000000d75c3c in NvmlInit () at nvml.c:87
#2  0x0000000000d757f0 in _cgo_4e2c63ea9b15_Cfunc_NvmlInit (v=0x400038d9a8) at /tmp/go-build/cgo-gcc-prolog:114
#3  0x000000000049013c in runtime.asmcgocall () at /home/ec2-user/.go/src/runtime/asm_arm64.s:1049
#4  0x00000040000a4a80 in ?? ()
Backtrace stopped: not enough registers or memory available to unwind further
```

So far so good. Next, I set a breakpoint on `fgets` and continued.

```c
(gdb) b fgets
Breakpoint 2 at 0xfffff7e13a90
(gdb) c
Continuing.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
WARNING:

You should always run with libnvidia-ml.so that is installed with your
NVIDIA Display Driver. By default it's installed in /usr/lib and /usr/lib64.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
time=2025-08-28T02:09:33.691Z level=INFO msg="Disconnected from Runtime Manager" reason="connection error: desc = \\"error reading server preface: EOF\\""
[Detaching after vfork from child process 43280]

Thread 3 "noded" hit Breakpoint 2, 0x0000fffff7e13a90 in fgets () from /lib64/libc.so.6
(gdb) bt
#0  0x0000fffff7e13a90 in fgets () from /lib64/libc.so.6
#1  0x0000000000d82440 in ?? ()
#2  0x0000000000d982c0 in nvmlInit_v2 ()
#3  0x0000000000d75c3c in NvmlInit () at nvml.c:87
#4  0x0000000000d757f0 in _cgo_4e2c63ea9b15_Cfunc_NvmlInit (v=0x400038d9a8) at /tmp/go-build/cgo-gcc-prolog:114
#5  0x000000000049013c in runtime.asmcgocall () at /home/ec2-user/.go/src/runtime/asm_arm64.s:1049
#6  0x00000040000a4a80 in ?? ()
Backtrace stopped: not enough registers or memory available to unwind further
(gdb)
```

The breakpoint got hit nicely, but wait, did you notice the difference?

It’s obvious. We got NVML warning here. Remember we didn’t see this warning in the repro. So this call to `fgets` is not the one getting stuck. If you look at the stacktrace carefully, you’ll see the return address is different too. Here’s the callstack of the repro: d82440 vs d824a4.

```c
#4  0x0000ffffaa8dfb20 in fgets () from /lib64/libc.so.6
#5  0x0000000000d824a4 in ?? ()
#6  0x0000000000d982c0 in nvmlInit_v2 ()
#7  0x0000000000d75c3c in NvmlInit () at nvml.c:87
```

The imagebase might be different because this is a different process? That’s a good point, but probably it’s not the case. Usually the imagebase is reused for efficiency, but in this case the address is only 0x64 bytes different. If the module is mapped onto a different address, the difference would be aligned with page size 4k.

So this is a different call to `fgets`. Good news is both calls are made from the same function that is called from `nvmlInit_v2` . You can get it because the return address of a function calling `fgets` is 0xd982c0 in both cases.

Fun time to dive into assembly! Let’s start from `nvmlInit_v2`.

```c
(gdb) disas nvmlInit_v2
Dump of assembler code for function nvmlInit_v2:
   0x0000000000d98260 <+0>:     stp     x19, x30, [sp, #-16]!
   0x0000000000d98264 <+4>:     adrp    x19, 0x1aa1000
   0x0000000000d98268 <+8>:     bl      0xd825a0 <stubSpinLock>
   0x0000000000d9826c <+12>:    ldr     x0, [x19, #1080]
   0x0000000000d98270 <+16>:    cbz     x0, 0xd982a0 <nvmlInit_v2+64>
   0x0000000000d98274 <+20>:    adrp    x1, 0x1234000
   0x0000000000d98278 <+24>:    mov     w19, #0x0                       // #0
   0x0000000000d9827c <+28>:    add     x1, x1, #0x168
   0x0000000000d98280 <+32>:    bl      0x4103a0 <dlsym@plt>
   0x0000000000d98284 <+36>:    cbz     x0, 0xd98290 <nvmlInit_v2+48>
   0x0000000000d98288 <+40>:    blr     x0
   0x0000000000d9828c <+44>:    mov     w19, w0
   0x0000000000d98290 <+48>:    bl      0xd825d0 <stubUnLock>
   0x0000000000d98294 <+52>:    mov     w0, w19
   0x0000000000d98298 <+56>:    ldp     x19, x30, [sp], #16
   0x0000000000d9829c <+60>:    ret
   0x0000000000d982a0 <+64>:    bl      0xd824e0
   0x0000000000d982a4 <+68>:    str     x0, [x19, #1080]
   0x0000000000d982a8 <+72>:    cbnz    x0, 0xd98274 <nvmlInit_v2+20>
   0x0000000000d982ac <+76>:    adrp    x0, 0x1234000
   0x0000000000d982b0 <+80>:    add     x0, x0, #0x28
   0x0000000000d982b4 <+84>:    bl      0x410270 <puts@plt>
   0x0000000000d982b8 <+88>:    mov     w19, #0x9                       // #9
   0x0000000000d982bc <+92>:    bl      0xd823d0
   0x0000000000d982c0 <+96>:    bl      0xd825d0 <stubUnLock>
   0x0000000000d982c4 <+100>:   mov     w0, w19
   0x0000000000d982c8 <+104>:   ldp     x19, x30, [sp], #16
   0x0000000000d982cc <+108>:   ret
```

Right above 0xd982c0, there is a BL to 0xd823d0, which must be the function call we’re looking for.

One disadvantage of gdb compared to Windows Debugger (ntsd/cdb/kd/windbg) is the `disas` command (`uf` in Windows debuggers) doesn’t work if there is no matching symbol on the target address. We need to use `x` command instead.

```c
(gdb) x/80i 0xd823d0
   0xd823d0:    sub     sp, sp, #0x330
   0xd823d4:    mov     x2, #0x83                       // #131
   0xd823d8:    adrp    x1, 0x1233000 <crypto/internal/fips140/rsa..stmp_0+96>
   0xd823dc:    add     x1, x1, #0xe18
...snip...
   0xd82428:    bl      0x4103c0 <strstr@plt>
   0xd8242c:    cbnz    x0, 0xd824a8
   0xd82430:    mov     x2, x20
   0xd82434:    mov     x0, x19
   0xd82438:    mov     w1, #0x100                      // #256
   0xd8243c:    bl      0x410480 <fgets@plt>
   0xd82440:    cbnz    x0, 0xd82420
   0xd82444:    mov     x0, x20
   0xd82448:    bl      0x4103d0 <pclose@plt>
   0xd8244c:    bl      0x410130 <getpid@plt>
   0xd82450:    mov     w2, w0
   0xd82454:    adrp    x1, 0x1233000 <crypto/internal/fips140/rsa..stmp_0+96>
   0xd82458:    mov     x0, x22
   0xd8245c:    add     x1, x1, #0xeb0
   0xd82460:    bl      0x4100e0 <sprintf@plt>
   0xd82464:    add     x1, x23, #0xea0
   0xd82468:    mov     x0, x22
   0xd8246c:    bl      0x410160 <popen@plt>
   0xd82470:    mov     x20, x0
   0xd82474:    cbz     x0, 0xd824b0
   0xd82478:    adrp    x21, 0x1233000 <crypto/internal/fips140/rsa..stmp_0+96>
   0xd8247c:    add     x21, x21, #0xee0
   0xd82480:    b       0xd82494
   0xd82484:    mov     x1, x21
   0xd82488:    mov     x0, x19
   0xd8248c:    bl      0x4103c0 <strstr@plt>
   0xd82490:    cbnz    x0, 0xd824c4
   0xd82494:    mov     x2, x20
   0xd82498:    mov     x0, x19
   0xd8249c:    mov     w1, #0x100                      // #256
   0xd824a0:    bl      0x410480 <fgets@plt>            // <----- this causes stuck in the repro
   0xd824a4:    cbnz    x0, 0xd82484
...snip...
```

Alright, we found the call to `fgets` that is stuck in the repro. Well, we could enjoy finding out why it went down a different path with/without systemd, but not today. Let’s focus on the original issue.

The next thing to find out is what’s this `fgets` call. Before asking AI for help, just spend some time on reading this assembly code. The hint is what happens before we call `fgets`. Around there, we can see calls to standard functions like `getpid`, `sprintf`, `popen`, `strstr`, etc. If you have some experience in C programming, you can easily draw a picture about the behavior. Here’s my thinking process:

* There’s `popen` before `fgets` . Probably `fgets` is to read the output from a process spawned by this `popen`
* Before `popen`, the function calls `sprintf`. This must be to construct a command-line string to pass to `popen`
* `getpid` before `popen` is to embed the current process’s PID into the command-line string

What does this mean? Always step back and remember the original issue. The problem is `fgets` never returns. And we just found out `nvmlInit_v2` spawns a process and reads the output via `fgets` , which gets stuck. No documentation about this behavior in NVIDIA’s reference. Who knows a simple initialization function spawns a child process!

The next question is of course, what process is spawned? You can go back to the shell and run `ps` command to find out, but hold on, let’s stick to code and figure out the command line string first. `sprintf` is our next target because it looks like constructing a command to execute.

I believe you know `sprintf`. To understand a command to be constructed, it’s better to see a format string, which is passed to `sprintf` as the second argument. By the way, one advantage of ARM64 compared to x86 is fixed-sized instructions. Another advantage is a straightforward calling convention. The second argument is stored in the R1 register. Let’s see that value. Here’s the assembly to call `sprintf`.

```c
   0xd82450:    mov     w2, w0
   0xd82454:    adrp    x1, 0x1233000 <crypto/internal/fips140/rsa..stmp_0+96>
   0xd82458:    mov     x0, x22
   0xd8245c:    add     x1, x1, #0xeb0
   0xd82460:    bl      0x4100e0 <sprintf@plt>
```

If you’re not familiar with ARM64 instructions, specs can be found [here](https://developer.arm.com/documentation/dui0489/i/arm-and-thumb-instructions/). If you know ARM64 instructions already, you’ll easily see the second argument is `0x1233000 + 0xeb0`. Let’s check what’s there.

```c
(gdb) x/s 0x1233000 + 0xeb0
0x1233eb0:      "lsof -p %d | tr -s ' ' | cut -d ' ' -f 9 "
```

This is apparently a template of a command-line string, and explains why we call `getpid` .

Now, it’s time to confirm this finding. Start Dynamo and see if we’re really running this command.

```c
[ec2-user@ip-172-31-15-164 ~]$ sudo systemctl restart dynamo
[ec2-user@ip-172-31-15-164 ~]$ ps -ef | grep noded
ec2-user   45772       1  1 03:32 ?        00:00:00 /opt/dynamo/noded -config=/opt/dynamo/config.json
ec2-user   45789   36391  0 03:32 pts/0    00:00:00 grep --color=auto noded
[ec2-user@ip-172-31-15-164 ~]$ ps -ef | grep 45772
ec2-user   45772       1  0 03:32 ?        00:00:00 /opt/dynamo/noded -config=/opt/dynamo/config.json
ec2-user   45780   45772  0 03:32 ?        00:00:00 sh -c lsof -p 45772 | tr -s ' ' | cut -d ' ' -f 9
ec2-user   45781   45780  0 03:32 ?        00:00:00 lsof -p 45772
ec2-user   45784   45781 96 03:32 ?        00:00:09 lsof -p 45772
ec2-user   45791   36391  0 03:32 pts/0    00:00:00 grep --color=auto 45772

```

We’re running that command indeed! The next thing to do is of course, to run the exact same command manually.

```c
[ec2-user@ip-172-31-15-164 ~]$ sh -c lsof -p 45772 | tr -s ' ' | cut -d ' ' -f 9
SIZE/OFF
denied)
denied)
denied)
...snip...
/usr/lib64/libtirpc.so.3.0.0
/usr/lib64/libselinux.so.1
/usr/lib/ld-linux-aarch64.so.1
/usr/lib64/gconv/gconv-modules.cache
pipe
pipe
```

It just worked. Weird. Anyway, we know the root cause is not in Dynamo but in `lsof` , which doesn’t finish if it’s spawned from Dynamo spawned via systemd. What’s next? Should we attach gdb to a `lsof` process to find out? Well, I could, but at this time, I took a different approach, `strace`. And it worked very well.

### Root cause

Let’s restart dynamo and run `strace` for `lsof`.

```c
[ec2-user@ip-172-31-15-164 ~]$ sudo systemctl restart dynamo
[ec2-user@ip-172-31-15-164 ~]$ ps -ef | grep noded
ec2-user   46510       1  2 03:47 ?        00:00:00 /opt/dynamo/noded -config=/opt/dynamo/config.json
ec2-user   46526   36391  0 03:47 pts/0    00:00:00 grep --color=auto noded
[ec2-user@ip-172-31-15-164 ~]$ ps -ef | grep 46510
ec2-user   46510       1  0 03:47 ?        00:00:00 /opt/dynamo/noded -config=/opt/dynamo/config.json
ec2-user   46519   46510  0 03:47 ?        00:00:00 sh -c lsof -p 46510 | tr -s ' ' | cut -d ' ' -f 9
ec2-user   46520   46519  0 03:47 ?        00:00:00 lsof -p 46510
ec2-user   46524   46520 92 03:47 ?        00:00:09 lsof -p 46510
ec2-user   46528   36391  0 03:47 pts/0    00:00:00 grep --color=auto 46510
[ec2-user@ip-172-31-15-164 ~]$ strace -p 46520
strace: Process 46520 attached
wait4(-1, ^Cstrace: Process 46520 detached
 <detached ...>
[ec2-user@ip-172-31-15-164 ~]$ strace -p 46524
close(503905516)                        = -1 EBADF (Bad file descriptor)
close(503905517)                        = -1 EBADF (Bad file descriptor)
close(503905518)                        = -1 EBADF (Bad file descriptor)
close(503905519)                        = -1 EBADF (Bad file descriptor)
close(503905520)                        = -1 EBADF (Bad file descriptor)
close(503905521)                        = -1 EBADF (Bad file descriptor)
...
```

Hmm, we found something really unusual. In the `lsof` process, a bunch of `close` are failing.

So, it’s finally time to debug `lsof`? Again, I could, but to be more practical and save time, I thought it’s time to ask AI about what’s a possible cause. And here’s the actual answer I got.

```c
Yes — that strace output is a big hint.

When you see lsof looping on

```

close(503905516) = -1 EBADF close(503905517) = -1 EBADF …

```

it means lsof is walking a huge range of possible fd numbers and calling close() on them one by one.

That is not normal fd enumeration (which should only read /proc/<pid>/fd). This is an old implementation detail of lsof:

Before using /proc/<pid>/fd, lsof tries to "sanitize" the fd table by closing everything above its stdio fds, to make sure it doesn’t inherit descriptors it shouldn’t.

It doesn’t ask the kernel what the max fd is, but instead guesses from getrlimit(RLIMIT_NOFILE).
```

There is what I haven’t exposed yet: the service definition of Dynamo, which is this:

```c
[ec2-user@ip-172-31-15-164 ~]$ cat /etc/systemd/system/dynamo.service
[Unit]
Description="Dynamo node service"
After=network-online.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=on-failure
RestartSec=5s
User=ec2-user
SyslogIdentifier=dynamo
WorkingDirectory=/opt/dynamo
KillSignal=SIGHUP
SyslogFacility=local7
ExecStart=/opt/dynamo/noded \\
  -config=/opt/dynamo/config.json
LimitNOFILE=infinity
LimitNPROC=infinity

[Install]
WantedBy=multi-user.target
```

Do you see anything unusual? It must be `LimitNOFILE=infinity`. There was no particular reason I added it. According to the AI’s answer, an old implementation of `lsof` guesses the max fd from `getrlimit(RLIMIT_NOFILE)` . In this case, it must be `RLIMIT_NOFILE = RLIM_INFINITY = 2^63-1` , meaning `lsof` is closing everything above its stdio fds below 2^63-1. This is the reason why Dynamo is stuck.

Alright, it seems that we found out the root cause, The next move is to confirm this. It’s easy. Let’s comment out `LimitNOFILE=infinity` and run Dynamo.

I don’t put the command output here because it’s obvious. The result is, it actually worked! Without `LimitNOFILE=infinity` , Dynamo just works normally.

What’s a conclusion here? It’s a bug in `lsof`? Basically yes, but remember what AI said: *This is an old implementation detail of lsof*. This implicates the latest `lsof` behaves differently. And remember I found this issue when I ran Dynamo on Amazon Linux ARM64, which recently started being supported. We’ve never seen this issue before, from x64 machines. Let’s check the version of `lsof`.

```c
[ec2-user@ip-172-31-15-164 ~]$ cat /proc/version
Linux version 6.1.147-172.266.amzn2023.aarch64 (mockbuild@ip-10-0-37-70) (gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-5), GNU ld version 2.41-50.amzn2023.0.3) #1 SMP Thu Aug  7 19:28:45 UTC 2025
[ec2-user@ip-172-31-15-164 ~]$ lsof -v
lsof version information:
    revision: 4.94.0
    latest revision: <https://github.com/lsof-org/lsof>
    latest FAQ: <https://github.com/lsof-org/lsof/blob/master/00FAQ>
    latest (non-formatted) man page: <https://github.com/lsof-org/lsof/blob/master/Lsof.8>
    constructed: Mon May 19 00:00:00 UTC 2025
    compiler: cc
    compiler version: 11.5.0 20240719 (Red Hat 11.5.0-5) (GCC)
...
```

It’s 4.94.0. How about another machine of x64 where Dynamo runs without any problem?

```c
ubuntu@dynamo:~$ cat /proc/version
Linux version 6.8.0-59-generic (buildd@lcy02-amd64-035) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubuntu) 2.42) #61-Ubuntu SMP PREEMPT_DYNAMIC Fri Apr 11 23:16:11 UTC 2025
ubuntu@dynamo:~$ lsof -v
lsof version information:
    revision: 4.95.0
    latest revision: <https://github.com/lsof-org/lsof>
    latest FAQ: <https://github.com/lsof-org/lsof/blob/master/00FAQ>
    latest (non-formatted) man page: <https://github.com/lsof-org/lsof/blob/master/Lsof.8>
    compiler: cc
    compiler version: 13.2.0 (Ubuntu 13.2.0-23ubuntu3)
```

Oops, it’s different: 4.94.0 vs 4.95.0!

Fortunately, unlike NVML, as you can see it in the output above, `lsof` is open-sourced. Actually [the release note of 4.95.0](https://github.com/lsof-org/lsof/releases/tag/4.95.0) mentions a fix for a bug that sound pretty much like our issue.

```
	[linux] use close_range instead of calling close repeatedly
	At the starting up, lsof closes its file descriptors greater
	than 2 by calling close(2) repeatedly. As reported in #186,
	it can take long time. Linux 5.9 introduced close_range(2).
	The new system call can close multiple file descriptors faster.
	@qianzhangyl reported the original issue (#186).
```

So I built `lsof` from source to see if the version 4.95.0 really fixed our issue (with uncommenting the line of `LimitNOFILE=infinity` of course). Unfortunately it didn’t. Thus I tried several version to find out which version fixed this issue. By the way, here are the commands to build `lsof`.

```c
$ git clone <https://github.com/lsof-org/lsof.git> -b 4.95.0
$ cd lsof
$ ./Configure linux
...
$ make -j4
...
$ ./lsof -v
lsof version information:
    revision: 4.95.0
    latest revision: <https://github.com/lsof-org/lsof>
    latest FAQ: <https://github.com/lsof-org/lsof/blob/master/00FAQ>
    latest (non-formatted) man page: <https://github.com/lsof-org/lsof/blob/master/Lsof.8>
    constructed by and on: ec2-user@ip-172-31-15-164.ec2.internal
    compiler: cc
    compiler version: 11.5.0 20240719 (Red Hat 11.5.0-5) (GCC)
...
```

Finally, I confirmed the issue is gone with 4.99.0 while the issue exists with 4.98.0. Let’s check [the release note of 4.99.0](https://github.com/lsof-org/lsof/releases/tag/4.99.0).

> ```
> 	[linux] Improve performance by using closefrom(). Closes #281.
> ```

The issue [281](https://github.com/lsof-org/lsof/issues/281) must be our issue. To really confirm, I could revert this patch and see, but I didn’t go that much further. Another mystery is why `lsof` 4.95.0 on Ubuntu x64 instance doesn’t hit this issue. That’s a very good question. I’ll dive into these another time.

Let’s recap:

* We saw an issue where Dynamo got stuck if it’s run through systemd on Amazon Linux ARM64.
* Dynamo calls `nvmlInit_v2`, which spawns `lsof` , which gets stuck
* lsof v4.94.0 has a bug that it attempts to close everything above its stdio fds
* Service definition of Dynamo specifies `LimitNOFILE=infinity` , meaning the upper limit of lsof’s close attempts is 2^63-1

Considering all the above, what’s a fix? I simply decided to specify `LimitNOFILE=1048576` instead of `infinity`. Again, there was no particular reason why I chose `infinity` from the beginning. Another possible approach is to apply this setting only if the version of `lsof` on the system is lower than 4.99.0, or set a custom value only while calling `nvmlInit_v2` , but I prefer the simplest fix.

This is just another day of debugging at Kinesis. Happy debugging!


# Biotech Is Ready to Save Lives. But Compute Is Holding It Back.

We stand on the threshold of one of the most profound leaps in human history.

AI is helping us decode the language of life itself. From folding proteins to designing molecules and mapping the causes of rare diseases, researchers are using the latest technological advances to accelerate scientific discovery and develop breakthrough treatments that were previously impossible to achieve. While the tools of modern biotech are changing medicine; they’re also changing what it means to be human.

But for the first time in history, progress toward these breakthroughs isn’t limited by scientific understanding. It’s limited by access to compute.

### The Future of Medicine Is Computational

A single human genome contains more than 3 billion base pairs. Decoding that data used to take years. Today, we can sequence a genome in hours, and AI is rapidly accelerating our ability to analyze and interpret it, often in days, sometimes minutes. But sequencing DNA is no longer the final goal. With AI, we're now asking these systems to interpret what the genetic code actually means.

Tools like AlphaFold and OpenFold have shown how neural networks can predict protein structures with remarkable accuracy, compressing decades of structural biology into hours of computation. AI-powered molecular simulations are speeding up early-stage drug discovery by screening candidate compounds at scale. And pattern recognition models are helping clinicians detect rare genetic disorders across vast, fragmented datasets, bringing new hope to patients who once went undiagnosed.

The era of precision medicine is just beginning, but its transformative potential hinges entirely on access to high-performance compute. This computational bottleneck affects researchers across the board. Even well-funded organizations [like the Chan Zuckerberg Initiative](https://www.businessinsider.com/mark-zuckerberg-priscilla-chan-gpus-recruitment-tool-chan-zuckerberg-initiative-2025-7) are prioritizing GPU access for their teams. This widespread demand for computing resources has created a fundamental challenge that threatens to limit the pace of medical breakthroughs.

### The Hidden Bottleneck No One Talks About

The cost of compute has become the silent constraint in modern science.

The same GPU clusters used to train language models are also needed to run biomedical AI. As demand skyrockets across every sector—from finance to entertainment—researchers are finding themselves priced out, queued up, or locked into rigid, expensive platforms.

Labs are postponing key analyses. Startups are redesigning their products around compute constraints. And sometimes, life-saving experiments are delayed indefinitely. This issue isn't because the science isn’t ready. The problem is the infrastructure isn’t ready.

When compute access becomes the bottleneck, human progress slows. When compute is wasted, live saving treatments are delayed.

### This Is a Global Problem. And It Needs a Global Solution.

Here’s the truth: the world doesn’t suffer from a lack of compute. It suffers from a failure to source and redistribute it to researchers at the forefront of medical research.

Across the planet, billions of dollars’ worth of CPUs and GPUs sit idle in data centers, offices, and even gaming consoles. Underutilized cloud inventory. Stranded enterprise servers. Consumer hardware with industrial-grade power. It’s all there—waiting.

What we lack is a unifying layer to make it usable.

Fixing problems at a global scale requires collaboration at a global scale. Scientists must be free to focus on discovery. Hardware providers should be able to contribute resources with minimal friction. What’s missing is the connective tissue: a new kind of digital infrastructure that makes compute accessible, affordable, and effortless to use—anywhere in the world.

### Kinesis Network: The Digital Infrastructure for Life-Saving AI

This is where Kinesis comes in.

Kinesis Network is building the critical digital backbone for AI-enabled biotech. We aggregate global compute capacity—from hyperscalers to edge devices—and route AI workloads in real time to the most available, cost-effective resources. Researchers simply upload their models and run.

No vendor lock-in. No orchestration headaches. No inflated bills.

Just pure, optimized performance—built for scientists, not sysadmins.

For the biotech ecosystem, this means:

* Running more simulations, faster.
* Scaling genomic analysis without hiring DevOps.
* Cutting compute costs by up to 99%, freeing budgets for research.

It means acceleration—without compromise.

### Everyone Can Play a Part

Here’s the remarkable part: you can be part of it too.

With Kinesis, anyone, anywhere in the world, can donate unused compute power to support the research they care about. Whether it’s cancer, Alzheimer’s, or rare genetic disorders, your idle machine can become part of a global mesh that fuels real scientific breakthroughs.

Just by running a lightweight application, your device can help power models that decode genomes, simulate drug interactions, or predict how a mutation might affect a protein. You don’t need to be a scientist to help cure disease. You just need a computer, and the willingness to contribute.

This is more than compute. It’s collaboration on lifesaving treatments at a planetary scale.

### A Shared Mission to Move Science Forward

Biotech has always been a long game. But today, time has never mattered more. Whether it's a child awaiting a rare disease diagnosis or a team racing to develop the next cancer therapy, every day of delay matters.

We believe that removing artificial bottlenecks from this process is one of the most meaningful contributions we can make to human progress. That’s why we’re building Kinesis. We are on a mission to support scientists, and accelerate the amazing work they are doing on behalf of the human species.

We don’t have the luxury to waste compute anymore.

Behind every delayed model is a patient.

Behind every slow simulation is a therapy that didn’t arrive in time.

And behind every bottleneck, there is an opportunity to do better.

Let’s fix this—together.


# DeepSeek & AI Compute: Disruption or Evolution?

DeepSeek’s latest AI breakthrough has stirred up conversations across the industry. Some call it a game-changer; others compare it to AI’s "Sputnik moment." The reaction has been swift—not just in AI circles but across the financial markets, with **over $600 billion wiped out from major tech stocks**, including **Nvidia**, **Microsoft**, **Alphabet**, and **Amazon**.

Nvidia, in particular, suffered **the biggest single-day loss in stock market history**, as investors reevaluated expectations about AI compute demand. The market panic stemmed from claims that DeepSeek’s model delivers comparable performance at a fraction of the compute cost, disrupting assumptions about the infrastructure requirements of cutting-edge AI.

But beyond the headlines and market volatility, what does this actually mean for AI infrastructure and compute efficiency?

Let’s break down what’s happening and what it means for the future of AI compute.

### **DeepSeek’s AI Training Efficiency: Impressive, But Not a Revolution**

DeepSeek trained its **671B parameter model using 2,048 GPUs over 57 days**, totaling **\~2.78 million GPU hours**. That’s an efficiency win compared to industry norms, but it doesn’t fundamentally change the compute landscape.

Key takeaways:

* **DeepSeek still required massive computational power.** Their process optimized GPU utilization, but did not eliminate the need for high-performance hardware.
* **This is an optimization, not a paradigm shift.** AI training remains an expensive, compute-heavy process, even with efficiency improvements.
* **DeepSeek leveraged Nvidia GPUs**—highlighting that current AI breakthroughs are still tied to the same core hardware ecosystem.

### **The Market Reacts: AI Disruption & the Compute Landscape Shift**

Following DeepSeek’s announcement, the stock market saw a sharp reaction, with over **$600 billion wiped out from major tech stocks** including Nvidia, Microsoft, Alphabet, and Amazon. Investors scrambled to reassess expectations about AI infrastructure needs and whether more efficient models could disrupt existing business models. However, history tells us that market shocks often overcorrect, and long-term compute demand remains resilient. AI adoption continues to expand, and while efficiency gains shift expectations, the need for scalable, cost-effective compute infrastructure is only growing.

* **More AI models, smarter architectures, and cost-efficient optimizations = Higher compute demand, not less.**
* **Nvidia will likely shift focus to inference acceleration.**
* **Decentralized compute solutions will become increasingly relevant.**

This is not a crisis for the compute industry—it’s an evolution.

### **The Real Cost in AI: Inference, Not Training**

While training is resource-intensive, inference is the **real long-term bottleneck**.

* Most AI companies do not train foundational models; they fine-tune existing ones.
* Training is typically a **one-time or infrequent cost**, whereas inference **scales linearly with usage**.
* Compute demand will continue to rise as AI adoption expands into real-world applications requiring continuous inference.

DeepSeek does not change this equation—it simply highlights that **optimization at every stage of AI compute is crucial.**

### **The Rise of Decentralized Compute**

One of the most overlooked impacts of AI efficiency improvements is the role of **decentralized compute networks**.

* **More efficient models fit better on smaller, distributed infrastructure.**
* **Gamer PCs, idle enterprise servers, and decentralized nodes** can now play a bigger role in AI processing.
* Hyperscalers will remain dominant, but **AI is no longer exclusive to massive centralized data centers.**

This shift is **great news for the entire AI ecosystem, including the open-source AI movement.** With DeepSeek making its model freely available (unlike GPT-4o), this raises new questions about the role of proprietary vs. open AI. Open-source AI could further fuel decentralized compute adoption, as companies look for cost-effective, flexible infrastructure alternatives to closed AI models —from startups building AI-driven products to enterprises integrating AI into their workflows, and decentralized compute providers enabling more efficient infrastructure. By reducing the reliance on hyperscalers and enabling more flexible compute access, this trend will lower costs and expand opportunities for AI adoption across industries.

### **Global AI Investment, Geopolitics & the Acceleration of Innovation**

DeepSeek proves one thing: **the AI race is heating up, and AI is now a geopolitical battleground.** The U.S. has imposed export controls on advanced AI chips to China, while China continues to make strides in AI research and alternative chip development. This competition will not only accelerate AI investments but also shape enterprise adoption strategies worldwide.

* China’s AI progress will likely accelerate **global AI investments**.
* The focus is shifting from raw compute power to **cost-efficient, scalable AI infrastructure.**

As AI compute becomes more efficient and widely accessible, innovation will accelerate. Startups will have lower barriers to entry, enterprises will be able to experiment with AI integrations more affordably, and decentralized compute networks will expand their role in supporting AI workloads. **This democratization of AI infrastructure will lead to new breakthroughs in model development**, fine-tuning, and application deployment.

### **What Comes Next? AI Compute Pricing & Sustainability**

The AI industry is moving toward a new phase where **efficiency, scalability, and decentralization** define success. But there’s another important factor—**AI compute pricing and sustainability.** If DeepSeek’s efficiency claims hold, will AI compute pricing come under pressure? Hyperscalers may respond by adjusting their pricing models, while decentralized compute providers could offer cost-competitive alternatives. Meanwhile, as AI models scale, energy consumption concerns grow—creating an opportunity for more sustainable, decentralized AI compute solutions. Key trends to watch:

* **LLMs are becoming commodities**—data ownership and enterprise adoption will be the real differentiators.&#x20;
* **Nvidia and other hardware players will double down on inference-focused chips.**
* **Decentralized compute networks will continue gaining traction** as AI models become more efficient.

### **Final Thoughts: The Future of AI Compute**

DeepSeek’s efficiency improvements are valuable, but they do not eliminate the need for massive compute power. Instead, they mark the beginning of a larger shift—one where geopolitics, open-source AI, pricing, and sustainability will shape the future of AI compute. They reinforce the importance of **optimization, inference efficiency, and scalable infrastructure**—areas where **decentralized compute can shine**.

At **Kinesis Network**, we’re building the future of AI compute—one that is **scalable, cost-effective, and decentralized.** As AI models evolve, so must the infrastructure that powers them. The future of AI isn’t just about who builds the biggest model—it’s about who can run them **the smartest.**

***

**Want to stay ahead of the AI compute revolution?** Follow [@Kinesis\_Network](https://twitter.com/kinesis_network) for more insights and updates on the future of compute.


# IEEE: The AI Boom Is Giving Rise to "GPU-as-a-Service

The industry harvests idle compute for AI startups that need it

The surge of interest in AI is creating a massive demand for computing power. Around the world, companies are trying to keep up with the vast amount of [GPUs](https://spectrum.ieee.org/tag/gpus) needed to power more and more advanced [AI models](https://spectrum.ieee.org/tag/ai-models). While [GPUs](https://spectrum.ieee.org/amd-mi300) are not the only option for [running an AI model](https://spectrum.ieee.org/ai-chip-sambanova), they have become the hardware of choice due to their ability to efficiently handle multiple operations simultaneously—a critical feature when developing [deep learning](https://spectrum.ieee.org/tag/deep-learning) models.

But not every AI startup has the capital to invest in the huge numbers of GPUs now required to run a cutting-edge model. For some, it’s a better deal to outsource it. This has led to the rise of a new business: GPU-as-a-Service (GPUaaS). In recent years, companies like [Hyperbolic](https://hyperbolic.xyz/), [Kinesis](https://kinesis.network/), [Runpod](https://www.runpod.io/), and [Vast.ai](https://vast.ai/) have sprouted up to remotely offer their clients the needed processing power.

While tech giants like [Amazon](https://spectrum.ieee.org/tag/amazon) or [Microsoft](https://spectrum.ieee.org/tag/microsoft) offering [cloud computing](https://spectrum.ieee.org/tag/cloud-computing) services own their infrastructure, smaller startups like Kinesis have created techniques to make the best out of the existing idle compute.

“*<mark style="color:green;">Businesses need compute. They need the model to be trained or their applications to be run; they don’t necessarily need to own or manage servers,</mark>*” says [Bina Khimani](https://www.linkedin.com/in/bkhimani/), co-founder of Kinesis.

[Studies](https://dl.acm.org/doi/10.1145/3597503.3639232) [have shown](https://www.nextplatform.com/2020/11/17/counting-the-cost-of-under-utilized-gpus-and-doing-something-about-it/) that more than half of the existing GPUs are not in use at any given time. Whether we’re talking personal computers or colossal server farms, a lot of processing capacity is under-utilized. What Kinesis does is identify idle compute—both for GPUs and CPUs—in servers worldwide and compile them into a single computing source for companies to use. Kinesis partners with universities, data centers, companies, and individuals who are willing to sell their unused computing power. Through a special software installed on their servers, Kinesis detects idle processing units, preps them, and offers them to their clients for temporary use.\
\
“*<mark style="color:green;">At Kinesis, we have developed technology to pool together fragmented, idle compute power and repurpose it into a server-less, auto-managed computing platform,</mark>*” says Khimani. Kinesis customers even have the possibility to choose from where they want their GPUs or CPUs to come.\
\
**AI Is Growing Faster Than Servers Can Keep Up**

GPUaaS is filling a growing gap in the AI industry. As learning models get more sophisticated, they need more power and an infrastructure that can process information faster and faster. In other words, without a sufficient number of GPUs, big AI models cannot operate—let alone improve. In October, OpenAI’s CEO, Sam Altman, [admitted](https://techcrunch.com/2024/10/31/openai-ceo-sam-altman-says-lack-of-compute-is-delaying-the-companys-products/) that the company was not releasing products as often as they had wished because they were facing “a lot of limitations” with their computing capacity.

Also in October, Microsoft’s CFO, Amy Woods, [told](https://www.microsoft.com/en-us/Investor/events/FY-2025/earnings-fy-2025-q1) the company’s investors in a conference call that demand for AI “*<mark style="color:green;">continues to be higher</mark>*” than their “*<mark style="color:green;">available capacity.</mark>*”

The biggest advantage of GPUaaS is economical. By removing the need to purchase and maintain the physical infrastructure, it allows companies to avoid investing in servers and IT management, and to instead put their resources toward improving their own deep learning, large language, and large vision models. It also lets customers pay for the exact amount of GPUs they use, saving the costs of the inevitable idle compute that would come with their own servers.

Server-less startups like Kinesis also claim to be friendlier to the environment than traditional cloud computing companies. By leveraging existing, unused processing units instead of powering additional servers, they say they significantly reduce energy consumption. In the last five years, big tech companies like [Google](https://spectrum.ieee.org/tag/google) and Microsoft have seen their [carbon emissions soar](https://www.npr.org/2024/07/12/g-s1-9545/ai-brings-soaring-emissions-for-google-and-microsoft-a-major-contributor-to-climate-change) due to the amount of energy consumed by AI. In response, some have turned their eyes to [nuclear energy](https://spectrum.ieee.org/nuclear-powered-data-center) to sustainably power their servers. Kinesis and other new startups offer a third route in which no further servers need to be plugged in.

“*<mark style="color:green;">Industry leaders are deeply committed to sustainability,</mark>*” Khimani says. “*<mark style="color:green;">With the focus on innovation and efficiency, they can optimize existing computing power that is already active and consuming energy, rather than continually adding more servers for every new application they run.</mark>*”

The growing demand for [machine learning](https://spectrum.ieee.org/tag/machine-learning) and colossal data consumption is turning GPUaaS into a very profitable tech sector. In 2023, the industry’s market size [was valued](https://www.fortunebusinessinsights.com/gpu-as-a-service-market-107797) at US $3.23 billion; in 2024, it grew to $4.31 billion. It’s expected to rise to $49.84 billion by 2032.\
\
“*<mark style="color:green;">The AI industry is rapidly advancing to a stage where the focus is shifting from merely building and training models to optimizing efficiency,</mark>*” Khimani says. “*<mark style="color:green;">Customers are increasingly asking questions like, ‘When training a new model, how can we do it extremely targeted and not consume an ocean of data that requires an enormous amount of compute and energy?’</mark>*”<br>


# Kinesis Network and Multiverse Computing Unite to Redefine AI Optimization

SEATTLE, WA, UNITED STATES, January 16, 2025

Amid headlines about corporations investing in nuclear power plants to meet the insatiable energy needs of AI workloads, two pioneering companies, [Multiverse Computing](https://multiversecomputing.com/) and [Kinesis Network Inc.](https://kinesis.network/) (“Kinesis”) have joined forces to provide a groundbreaking alternative—optimizing AI performance while drastically reducing resource consumption. The partnership comes as demand for AI capabilities grows exponentially, and organizations worldwide face mounting challenges related to power demands for these models.\
\
**Tackling Two Sides of the Same Problem**\
Multiverse Computing, a global leader in quantum and high-performance computing solutions, has been at the forefront of optimizing large language models (LLMs) to deliver improved performance and efficiency. By using advanced quantum-inspired algorithms, Multiverse Computing enhances the training and inference processes of LLMs, reducing computation time and energy requirements without compromising accuracy. The company’s compression software CompactifAI uses quantum-inspired tensor networks to improve the efficiency and performance of AI models. Shrinking these models reduces the compute power required to run them, which in turn reduces operating costs.\
\
“*<mark style="color:green;">We have found the perfect partner for our first significant U.S. partnership</mark>*<mark style="color:green;">,</mark>” said Enrique Lizaso Olmos, CEO and co-founder of Multiverse Computing. “*<mark style="color:green;">This collaboration will power new use cases for AI companies with models currently limited by power and compute requirements.</mark>*”\
\
Kinesis specializes in compute optimization, pooling distributed unused computing resources to maximize efficiency and minimize waste. Kinesis’ platform intelligently allocates workloads, ensuring that computational resources are used to their fullest potential. This dynamic resource optimization reduces infrastructure costs and aligns with global sustainability goals by decreasing carbon footprints.\
\
Kinesis’s innovative compute platform has also captured the attention of leading-edge organizations such as nCorium, Rare Compute, and Liminal Capital. By leveraging Kinesis’s optimization technology, these forward-thinking organizations aim to advance their sustainability journey while enhancing computational efficiency and reducing costs—without compromising performance. This growing interest from diverse industries highlights the versatility and transformative potential of Kinesis's approach to compute access and optimization.\
\
**A Collaborative Solution for Joint Customers**\
Together, Multiverse Computing and Kinesis are solving a pressing industry challenge: harnessing the power of AI while mitigating the environmental and financial costs associated with significant computational demands. By integrating their respective expertise in LLM and compute optimization, the two companies offer a comprehensive solution for customers seeking to drive innovation efficiently and sustainably.\
\
“<mark style="color:green;">AI workloads are pushing the limits of what current infrastructure can handle,</mark>” said Victor Gaspar, Chief Sales Officer of Multiverse Computing. “<mark style="color:green;">Our partnership with Kinesis allows us to offer a comprehensive approach to optimization, addressing both the performance of AI models and the utilization of compute resources. This collaboration ensures our customers can innovate without compromising on cost or sustainability.</mark>”\
\
Multiverse Computing recently completed the AWS Gen AI Accelerator program which included AWS credits, mentorship and resources to expand AI and machine learning (ML) technologies. Kinesis is an official AWS partner.\
\
“<mark style="color:green;">The synergy between Kinesis and Multiverse Computing is a game-changer for the AI industry,</mark>” said Bina Khimani, Chief Product and Revenue Officer of Kinesis. “<mark style="color:green;">By tackling inefficiencies in both model performance and computational resource allocation, we’re enabling enterprises to achieve more with less. It’s a powerful step forward for both business and the planet.</mark>”\
\
**Driving Efficiency and Sustainability**\
This partnership comes at a critical time when industries face increasing scrutiny over the environmental impact of AI technologies. By reducing energy consumption and optimizing resource usage, Multiverse Computing and Kinesis are driving operational efficiencies and setting a benchmark for sustainable AI practices.\
\
Together, Multiverse Computing and Kinesis are charting a course toward a more efficient and sustainable AI future, proving that innovation doesn’t have to come at the cost of the planet.\
\
**About Kinesis Network**\
Kinesis Network is a revolutionary platform that solves the critical shortage of computing access for AI developers, researchers, and enterprises. By connecting GPU providers with those who need computing power, Kinesis makes high-performance computing more efficient and accessible. Trusted by enterprises, academic institutions, and research organizations, Kinesis democratizes access to the vital infrastructure needed for AI development and data-intensive research. For more information about how Kinesis Network solves the compute access shortage, visit <https://kinesis.network/>.\
\
**About Multiverse Computing**\
Founded in 2019, Multiverse Computing is a leading quantum software company that combines quantum computing, AI and optimization solutions to solve complex industry challenges. The company’s team of over 160 full-time employees, comprising 40% PhDs and representing more than 43 nationalities, has developed CompactifAI, an LLM compressor which uses quantum-inspired tensor networks to make large language models (LLMs) more efficient and portable, reducing size by over 90%, with only a 2 – 3% drop in accuracy, and with over 50% savings in retraining and inference costs. Multiverse Computing enables organizations to optimize their AI models and computing resources for enhanced performance and efficiency. For more information, visit [www.multiversecomputing.com](http://www.multiversecomputing.com/).


# The Fundamentals of Web3: Revolutionizing the Internet with Ownership and Community

#### **What is Web3?**

Web3 is a multi-faceted term that encompasses a broad range of technologies and ideas aimed at decentralizing the internet. To better understand it, let’s look at how Web3 builds on its predecessors—Web1 and Web2—while advancing a new vision for how people interact online, with more user control over data, identity, and assets.

**Web1** was the first phase of the internet, characterized by static, read-only web pages. **Web2**, which followed, introduced interactive, dynamic experiences but brought about centralized control by large platforms. In contrast, **Web3** seeks to create a decentralized internet, where blockchain technology enables users to interact directly, without intermediaries, and with enhanced control over their data and digital presence.

Web3 envisions a trustless, permissionless internet where digital assets, data, and identities are directly controlled by individuals. This model is often described as the *ownership economy*, enabling users to own and control their data and digital assets through technologies like decentralized finance (DeFi) and non-fungible tokens (NFTs). Additionally, it promotes an interoperable and user-centric web, allowing for seamless interactions across platforms while empowering users to participate in decision-making processes via decentralized governance.

***

#### **The Evolution of the Web**

1. **Web1** – The “Read-Only” Web: Early websites displayed static content that users could only read or view.
2. **Web2** – The “Read-Write” Web: Enabled dynamic content and interactivity, which allowed users to participate in social networks, online marketplaces, and more.
3. **Web3** – The “Read-Write-Own” Web: Empowers users to own, control, and monetize their data, assets, and digital presence.

By building on blockchain and decentralized technologies, Web3 has the potential to redefine industries and reshape the internet as we know it.

***

#### **Core Principles of Web3**

1. **Decentralization**: Unlike Web2 platforms that are owned and operated by central entities, Web3 applications are often decentralized, distributing control across participants rather than a single authority.
2. **User Sovereignty**: Web3 gives users control over their digital assets, data, and identity. This is often achieved through cryptographic wallets, where only the user has access.
3. **Trustless Transactions**: Transactions on Web3 platforms don’t require third-party intermediaries. Instead, smart contracts—self-executing agreements on the blockchain—enable secure peer-to-peer interactions.
4. **Interoperability**: Web3 promotes interoperability, meaning different applications and platforms can connect and share information seamlessly. This allows users to move digital assets and data across ecosystems without friction.
5. **Transparency**: Since blockchain transactions are publicly recorded, Web3 applications are inherently transparent, allowing users to verify the validity of transactions and governance activities.

***

#### **Key Sectors in Web3**

Web3 spans a range of sectors, each with distinct applications and use cases. Here are a few examples that highlight the diversity of Web3’s potential:

* **DeFi (Decentralized Finance)**: Offers financial services without intermediaries, allowing for peer-to-peer lending, borrowing, and trading on decentralized platforms.
* **DePIN (Decentralized Physical Infrastructure Networks)**: Powers real-world infrastructure like telecommunications, cloud storage, and compute networks by incentivizing resource sharing.
* **NFTs (Non-Fungible Tokens)**: Enables verifiable ownership of digital assets such as art, music, collectibles, and real estate in the digital space.
* **Social & Creator Economies**: Empowers creators to monetize their work directly through tokenized communities, where fans and followers gain ownership in the creator's success.

These sectors highlight Web3’s versatility, reshaping traditional industries from finance and media to infrastructure and beyond.

***

#### **Community-Driven Funding and Ownership**

One of the defining features of Web3 is its approach to funding and ownership. Unlike Web2 companies, which often rely on centralized funding from venture capital, Web3 projects tend to involve the community early on. This community-first model allows for early user participation, giving community members a vested interest in a project’s success and fostering a collaborative environment.

**Utility tokens** are frequently used in Web3 to provide holders access to specific functions within the ecosystem, rather than being a form of ownership. These tokens often serve as governance rights, staking assets, or utility credits, allowing holders to interact with the network while aligning with the project’s purpose. For instance, community staking or participation-based incentives encourage early adopters to actively engage with the project without relying on speculative token sales. This decentralized approach to funding and ownership enables community members to directly support the project’s growth, fostering a more equitable ecosystem.

***

#### **New Business Models in Web3**

In addition to technical advancements, Web3 introduces innovative business models that change how value is created and shared:

* **Token Economies**: Tokenized ecosystems incentivize participation by rewarding users with tokens for their contributions.
* **DAOs (Decentralized Autonomous Organizations)**: Enable community governance by allowing token holders to vote on key decisions, making the community part-owners of the project.
* **Play-to-Earn and Participate-to-Earn**: These models reward users for engaging with the ecosystem, be it through gaming, community participation, or content creation.
* **Data Sovereignty**: Web3 allows users to retain control over their personal data, with some models allowing users to earn from their own data.

These models challenge traditional approaches by prioritizing community involvement, shared ownership, and equitable value distribution, offering a fresh approach to how digital platforms operate.

***

#### **Looking Ahead**

Web3 represents a paradigm shift that goes beyond mere technological advancements. It fosters a new relationship between users, platforms, and digital assets, empowering individuals to take control of their data and assets while participating in decentralized economies. As Web3 continues to evolve, it will likely bring about a wave of innovation across sectors and industries, presenting new opportunities and challenges. Embracing Web3’s principles offers a glimpse into a future internet that is more decentralized, transparent, and community-oriented, with significant implications for society and the global economy.


# Kinesis Network Saves Costs

In recent years, serverless computing has emerged as a transformative approach to running applications and workloads in the cloud. This model shifts responsibility for infrastructure provisioning and management from the end user to the cloud provider. In this post, we are exploring how Kinesis Network with its serverless architecture saves costs.&#x20;

**1. Over-Provisioning and Idle Capacity**

One of the most notable causes of waste in an instance-based computing model is over-provisioning. When organizations provision specific virtual machines or containers, they frequently allocate more resources—CPU, memory, or storage—than the application actively needs. This over-allocation often aims to ensure enough capacity is available for peak usage, even if those peaks only occur sporadically. Unfortunately, during low-traffic intervals, these resources remain largely underutilized, creating wasted capacity. Our experience has shown us that a large portion of instances see less than 20% utilization on average, sometimes as bad as only 1%. In contrast, a serverless environment scales automatically based on demand. **The Kinesis Network spins up or tears down resources as needed, eliminating over-provisioning and shrinking the idle footprint significantly.**

**2. Pay-For-Idle vs. Pay-Per-Use**

Hand-in-hand with over-provisioning is the pay-for-idle model inherent in instance-based services. Organizations pay for entire virtual machines regardless of whether they are actively processing tasks or sitting idle. This can be a significant cost drain for applications with unpredictable traffic or infrequent usage patterns. **By contrast, Kinesis services follow a pay-per-use billing model.** This means that costs accrue only when the application processes requests. With no fixed cost for idle time, developers can benefit from substantial cost savings, making serverless the more financially efficient option.

**3. Operational Complexity**

In an instance-based model, DevOps teams are responsible for provisioning, configuring, patching, and maintaining virtual machines. This operational complexity often leads to less predictable results and potential inefficiencies. Each instance must be monitored, and scaling must be managed carefully. This level of manual oversight not only consumes time and resources but also increases the likelihood of human error. In our serverless architecture, **Kinesis Network handles most of these operational responsibilities—provisioning, scaling, fault tolerance, and more.** The result is less overhead in terms of both personnel and budget, leading to a more focused environment where developers can prioritize core business logic.

**4. Environmental Impact**

The wasteful nature of running underutilized or idle virtual machines has broader implications beyond cost. Modern data centers require substantial energy resources to power servers and maintain cooling systems. When capacity is over-allocated, these underutilized servers continue to consume power, leaving a significant carbon footprint. Kinesis Network, with its on-demand approach, reduces total operating hours of hardware. **Because Kinesis Network allocate resources dynamically, computing resources remain dormant until needed, cutting down on energy usage and subsequent environmental impact.**

**5. Flexibility and Agility**

From a software development perspective, serverless computing enables agile development. Functions can be deployed quickly without detailed infrastructure management, allowing teams to iterate faster. In an instance-based setup, teams must often navigate lengthy processes to provision and configure additional instances or adjust the size of existing ones. This overhead can stifle innovation and extend deployment cycles. **In contrast, Kinesis Network streamlines these processes, ensuring teams can rapidly experiment, test, and roll out new features with fewer constraints—and less waste.**

***

Despite these advantages, it's important to note that serverless computing isn't a silver bullet for all workloads. Long-running processes or applications with consistent, predictable loads might still benefit from instance-based deployments. However, for the vast majority of modern applications with variable workloads, serverless architectures offer a more environmentally sustainable approach to cloud computing.


# Powerlifting with Kinesis Network

Kinesis Network is designed to be flexible and multi-purpose. However, some workloads stand out to benefit most from the capabilities of Kinesis Network: Applications that  often rely on **parallel computation** (splitting large tasks into smaller ones) and **optimized hardware acceleration.**

Below are some common examples of workloads or applications that tend to push CPUs and GPUs to their limits:

### 1. AI & Machine Learning

#### Neural Network Training

* **What It Is**: Training large-scale models (e.g., convolutional neural networks for image recognition, transformers for language tasks) involves iterating over massive datasets and performing billions of floating-point operations.
* **Why It’s Intensive**: Each forward and backward pass can update millions or even billions of parameters. GPUs excel at this kind of parallelizable matrix multiplication.
* **Examples**:
  * **Image Classification** (ResNet, EfficientNet) on platforms like ImageNet.
  * **Large Language Models** (GPT-style), which can have hundreds of billions of parameters.
  * **Recommendation Systems** at companies like Netflix or YouTube, which process user activity logs in real time to update models.

#### Inference & Real-Time Prediction

* **What It Is**: Once models are trained, they need to make predictions on new data quickly.
* **Why It’s Intensive**: High-traffic systems (like voice assistants, search engines, or real-time translation services) can receive millions of queries per second. Optimizing inference—often on GPUs or specialized hardware (FPGAs, TPUs)—is critical for low latency.
* **Examples**:
  * **Virtual Assistants** (Possible alternatives to Siri, Alexa, Google Assistant).
  * **Security Systems** (Real time analysis of security footage, pattern matching)

#### Reinforcement Learning & Robotics

* **What It Is**: Training agents to interact with environments (e.g., game playing, industrial robots).
* **Why It’s Intensive**: Simulation-based training (like AlphaGo/AlphaZero) can require playing millions of matches or environment steps.
* **Examples**:
  * **Gaming engines like Go or Chess** playing against themselves.
  * **Industrial Robotics** where digital twins simulate thousands of robotic arm movements to optimize tasks.

***

### 2. Scientific Simulations & High-Performance Computing (HPC)

#### Weather Forecasting & Climate Modeling

* **What It Is**: Simulations of the Earth’s atmosphere, oceans, and land processes.
* **Why It’s Intensive**: These models involve solving partial differential equations across 3D grids that can contain billions of cells. Tiny time steps are used for accuracy, leading to large computational workloads.
* **Examples**:
  * **Weather forecasting research, hurricane simulations, early warning systems.**

#### Computational Fluid Dynamics (CFD)

* **What It Is**: Numerical analysis and data structures to analyze fluid flows—key in engineering (aerospace, automotive).
* **Why It’s Intensive**: Accurate CFD often requires extremely fine meshes or grids to capture turbulence and boundary layers, resulting in massive numerical calculations.
* **Examples**:
  * **Aircraft Design**
  * **Vehicle Aerodynamics** (simulating airflow around cars for optimizing fuel economy).

#### Astrophysics & Cosmology

* **What It Is**: Simulating large-scale structures in the universe (e.g., galaxy formation), star evolution, black holes, and gravitational waves.
* **Why It’s Intensive**: Interactions among billions of particles or elements, along with complex physical laws (general relativity, plasma physics), make these simulations extremely heavy.
* **Examples**:
  * **Simulations of Galaxy Clusters** by universities and research institutes.
  * **Studying Black Hole Mergers**

#### Nuclear & Particle Physics

* **What It Is**: Modeling subatomic particle interactions, nuclear reactor cores, or accelerator experiments.
* **Why It’s Intensive**: Requires quantum-level physics, Monte Carlo methods, and/or large-scale iterative solvers.
* **Examples**:
  * **Simulating particle collisions**
  * **Fusion reactor simulations**

***

### 3. Visual Effects and 3D Rendering

#### Film & Animation Rendering

* **What It Is**: Creating photorealistic images and animations.
* **Why It’s Intensive**: Global illumination, ray tracing, and advanced physics-based lighting models require evaluating complex mathematical functions for each pixel. Frames can take hours each to render at high quality.
* **Examples**:
  * **Production** of feature films.
  * **Blender’s Cycles** (open-source) for CPU/GPU path tracing.

***

### 4. Protein Folding & Other Bioinformatics

#### Protein Structure Prediction

* **What It Is**: Determining how a protein’s amino acid chain folds into a 3D structure—crucial for understanding biological functions and designing drugs.
* **Why It’s Intensive**: The potential configuration space is astronomically large. Advanced methods (like AlphaFold) use deep learning models that require significant GPU resources.
* **Examples**:
  * **Predicting structures for nearly all known proteins.**
  * **Volunteer computing for protein folding research.**

#### Genome Sequencing & Assembly

* **What It Is**: Processing raw sequencing reads to reconstruct whole genomes (e.g., human, plant, bacterial).
* **Why It’s Intensive**: Datasets can easily reach terabytes in size. Algorithms like de novo assembly or alignment-based methods (e.g., Bowtie, BLAST) require large HPC clusters.
* **Examples**:
  * **Large-scale sequencing projects** (e.g., 1000 Genomes, Cancer Genomics).
  * **Metagenomic Studies** analyzing entire microbial communities.

#### Molecular Dynamics Simulations

* **What It Is**: Simulating the movement of atoms in molecules or complexes over time.
* **Why It’s Intensive**: Calculating forces and interactions at each step for millions of atoms demands CPU/GPU acceleration (e.g., GROMACS, NAMD).
* **Examples**:
  * **Drug Discovery** (predicting how small molecules bind to protein targets).
  * **Basic Biophysics Research** on membrane channels or virus capsids.

***

### 5. Big Data Analytics & Data Processing

#### Distributed Computing Frameworks

* **What It Is**: Systems like Apache Hadoop and Spark split massive datasets across many nodes for parallel processing.
* **Why It’s Intensive**: Operations like sorting, aggregating, or joining large tables can involve scanning petabytes of data.
* **Examples**:
  * **ETL Pipelines** for enterprise data lakes.
  * **Social Media Analytics** at companies like Twitter or LinkedIn, which process billions of events daily.

#### Graph Processing

* **What It Is**: Analyzing node-link structures to find patterns (e.g., community detection, shortest paths, graph embeddings).
* **Why It’s Intensive**: Graph algorithms can be complex (e.g., PageRank), with large real-world graphs (millions or billions of nodes/edges).
* **Examples**:
  * **Social Graph** for friend recommendations.

#### Real-Time Stream Processing

* **What It Is**: Handling data that arrives continuously (logs, sensor data, click streams) for immediate analytics and alerts.
* **Why It’s Intensive**: Requires fast ingestion, transformations, and real-time dashboards. Latency constraints necessitate efficient CPU/GPU usage.
* **Examples**:
  * **Financial Tick Data** in high-frequency trading.
  * **IoT Sensor Streams** in factories or connected devices.

***

### 6. Cryptography & Security

#### Encryption / Decryption at Scale

* **What It Is**: Securing large volumes of data in motion (TLS/SSL connections) and at rest (disk encryption).
* **Why It’s Intensive**: Bulk operations on huge data sets, but modern CPU instruction sets (AES-NI) and hardware accelerators help.
* **Examples**:
  * **VPN Gateways** handling encrypted connections for thousands of users.

#### Password Cracking & Security Auditing

* **What It Is**: Testing password strength by trying many possibilities (brute force) or using dictionary-based attacks.
* **Why It’s Intensive**: GPU-acceleration (e.g., via Hashcat) can test billions of hashes per second.
* **Examples**:
  * **Penetration Testing** for corporate security.
  * **Law Enforcement** accessing encrypted devices with court authorization.

***

### 7. Financial Modeling & Quantitative Analysis

#### Monte Carlo Simulations

* **What It Is**: Statistical simulations for risk assessment, derivative pricing (e.g., options, bonds), and portfolio optimization.
* **Why It’s Intensive**: Accurate results often require millions of iterations, each involving complex financial models.
* **Examples**:
  * **Derivative Pricing** of complex instruments (e.g., exotic options).
  * **Value at Risk (VaR)** calculations across large portfolios in investment banks.

#### High-Frequency Trading (HFT)

* **What It Is**: Automated trading strategies that react to market changes in microseconds or nanoseconds.
* **Why It’s Intensive**: The time factor is critical. Firms invest in specialized HPC clusters, FPGAs, or ASICs to reduce latency.
* **Examples**:
  * **Quant Funds** (Renaissance Technologies, Two Sigma) using large computing clusters.
  * **Market Making** requiring real-time price updates and predictive models.

***

### 8. Computer-Aided Design & Engineering (CAD/CAE)

#### Finite Element Analysis (FEA)

* **What It Is**: Breaking down complex structures into smaller elements to analyze stress, strain, heat transfer, etc.
* **Why It’s Intensive**: Large models with fine meshes require iterative solvers and matrix operations. Parallel processing across CPU cores or GPUs is often used.
* **Examples**:
  * **Automotive Crash Simulations** (ANSYS, LS-DYNA).
  * **Aerospace Structural Analysis**

#### Generative Design & Topology Optimization

* **What It Is**: Algorithms that iteratively suggest new designs based on performance goals (weight, strength, efficiency).
* **Why It’s Intensive**: Each iteration requires an analysis, which is repeated dozens or hundreds of times.
* **Examples**:
  * **Light weighting** automotive parts for better fuel efficiency.
  * **Architectural Design** for optimized building layouts (Autodesk’s Generative Design).

***

### 9. Video Encoding & Transcoding

#### High-Resolution Video (4K/8K)

* **What It Is**: Encoding large video files for streaming or storage using codecs like H.264, HEVC, VP9, AV1.
* **Why It’s Intensive**: Each frame undergoes complex compression algorithms. More pixels (4K/8K) = more data. Real-time or batch encoding at scale can stress CPU/GPU clusters.
* **Examples**:
  * **Media encoding** to optimize storage and delivery for various screen sizes.

***

### 10. Real-Time Simulation & Digital Twins

#### Smart City & Factory Simulations

* **What It Is**: Digital replicas of physical environments (e.g., a production line, traffic network) coupled with real-time sensor data.
* **Why It’s Intensive**: Simulating thousands of moving parts, IoT data streams, and complex event processing requires HPC-level resources.
* **Examples**:
  * **Digital twins for factory floors**, integrating robotics and sensor feedback.
  * **Urban Planning** tools simulating traffic flow, infrastructure loads, and environmental impact.

#### Automotive & Aerospace Digital Twins

* **What It Is**: Real-time virtual counterparts of vehicles or aircraft to test system updates, maintenance, and design changes.
* **Why It’s Intensive**: Must incorporate physics, AI-based control systems, and potentially large sensor datasets from the real-world counterpart.
* **Examples**:
  * Sports teams running simulations during races for strategy.


