# Register an app

POST

/v1/connection-apps

```sh
curl --request POST \
  --url https://example.com/v1/connection-apps \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{ "app": "example", "label": "example", "oauth2": { "clientId": "example", "clientSecret": { "envName": "example", "name": "example" }, "tenant": "example" }, "githubApp": { "appNumber": 1, "slug": "example", "clientId": "example", "clientSecret": { "envName": "example", "name": "example" }, "privateKey": { "envName": "example", "name": "example" }, "webhookSecret": { "envName": "example", "name": "example" } }, "projects": [ "example" ] }'
```

Registers the tenant’s own client at an app. Store its secrets first; the registration references them by name. The response gives the `redirectUrl` and `noticesUrl` to enter at the app. Tenant admins only.

## Authorizations

- **[bearerAuth](https://docs.kindgi.com/v0.4.0/reference/api.md#bearerauth)**

## Parameters

### Header Parameters

**Idempotency-Key**

string

\>= 1 characters

Caller-supplied idempotency key, scoped to the caller: the same key from someone else in the tenant is their own request (from 0.1.5). Retries with the same key return the original response byte-identical (per `docs/API-ROUTE-CONVENTIONS.md` §3.1). A retry sent while the first request still runs is answered `409 idempotency-key-in-flight` with `Retry-After`, on a runtime whose store holds keys (from 0.1.5); retry after it, and you get the first request’s answer. An answer that carries a secret (a new API key or public run token, a session token, a generated signing secret) isn’t kept: a retry gets `409 idempotency-key-replay-withheld`, with the first request’s status and when it succeeded (from 0.1.5). A request whose key store can’t be read before it runs is answered `503 idempotency-store-unavailable` with `Retry-After`: nothing ran, so retry with the same key (from 0.3.0).

## Request Bodyrequired

application/json

Give `oauth2` for an app that signs in with OAuth, `githubApp` for a GitHub App.

object

**app**

required

The app’s entry id.

string

/^\[a-z]\[a-z0-9-]{1,31}$/

**label**

string

<= 200 characters

**oauth2**

object

**clientId**

required

string

**clientSecret**

required

A secret by name in the deployment’s secrets store, resolved at tenant scope. The registration keeps only this reference, never the value.

object

**envName**

required

string

/^\[a-z]\[a-z0-9-]{0,62}$/

**name**

required

string

\>= 1 characters <= 256 characters

**tenant**

The directory the app signs people in at, for an app whose sign-in names one: Microsoft’s tenant id or domain for a single-tenant app, `organizations`, `common` or `consumers` for a multi-tenant one. Required when the app’s entry has no default, refused for an app without one (`connection-app-invalid`, `details.field` `oauth2.tenant`).

string

\>= 1 characters <= 253 characters

**githubApp**

object

**appNumber**

required

integer

\>= 1

**slug**

required

The app’s slug.

string

**clientId**

required

string

**clientSecret**

required

A secret by name in the deployment’s secrets store, resolved at tenant scope. The registration keeps only this reference, never the value.

object

**envName**

required

string

/^\[a-z]\[a-z0-9-]{0,62}$/

**name**

required

string

\>= 1 characters <= 256 characters

**privateKey**

required

A secret by name in the deployment’s secrets store, resolved at tenant scope. The registration keeps only this reference, never the value.

object

**envName**

required

string

/^\[a-z]\[a-z0-9-]{0,62}$/

**name**

required

string

\>= 1 characters <= 256 characters

**webhookSecret**

required

A secret by name in the deployment’s secrets store, resolved at tenant scope. The registration keeps only this reference, never the value.

object

**envName**

required

string

/^\[a-z]\[a-z0-9-]{0,62}$/

**name**

required

string

\>= 1 characters <= 256 characters

**projects**

The projects whose runs may use its connections. Leave out for every project.

Array\<string>

\>= 1 items <= 500 items

##### Examplegenerated

```json
{
  "app": "example",
  "label": "example",
  "oauth2": {
    "clientId": "example",
    "clientSecret": {
      "envName": "example",
      "name": "example"
    },
    "tenant": "example"
  },
  "githubApp": {
    "appNumber": 1,
    "slug": "example",
    "clientId": "example",
    "clientSecret": {
      "envName": "example",
      "name": "example"
    },
    "privateKey": {
      "envName": "example",
      "name": "example"
    },
    "webhookSecret": {
      "envName": "example",
      "name": "example"
    }
  },
  "projects": [
    "example"
  ]
}
```

## Responses

### 201

App registered.

application/json

A tenant’s registration of its own client at an app. Secrets are references, never values.

object

**appId**

required

string format: uuid

**app**

required

The app’s entry id.

string

**label**

string

**oauth2**

object

**clientId**

required

string

**clientSecret**

required

A secret by name in the deployment’s secrets store, resolved at tenant scope. The registration keeps only this reference, never the value.

object

**envName**

required

string

/^\[a-z]\[a-z0-9-]{0,62}$/

**name**

required

string

\>= 1 characters <= 256 characters

**tenant**

The directory the app signs people in at, for an app whose sign-in names one: Microsoft’s tenant id or domain for a single-tenant app, `organizations`, `common` or `consumers` for a multi-tenant one. Required when the app’s entry has no default, refused for an app without one (`connection-app-invalid`, `details.field` `oauth2.tenant`).

string

\>= 1 characters <= 253 characters

**githubApp**

object

**appNumber**

required

integer

\>= 1

**slug**

required

The app’s slug.

string

**clientId**

required

string

**clientSecret**

required

A secret by name in the deployment’s secrets store, resolved at tenant scope. The registration keeps only this reference, never the value.

object

**envName**

required

string

/^\[a-z]\[a-z0-9-]{0,62}$/

**name**

required

string

\>= 1 characters <= 256 characters

**privateKey**

required

A secret by name in the deployment’s secrets store, resolved at tenant scope. The registration keeps only this reference, never the value.

object

**envName**

required

string

/^\[a-z]\[a-z0-9-]{0,62}$/

**name**

required

string

\>= 1 characters <= 256 characters

**webhookSecret**

required

A secret by name in the deployment’s secrets store, resolved at tenant scope. The registration keeps only this reference, never the value.

object

**envName**

required

string

/^\[a-z]\[a-z0-9-]{0,62}$/

**name**

required

string

\>= 1 characters <= 256 characters

**projects**

The projects whose runs may use its connections. Absent: every project.

Array\<string>

**redirectUrl**

required

Give this to the app as its redirect URL: a GitHub App’s Callback URL, with “Request user authorization (OAuth) during installation” ticked.

string format: uri

**noticesUrl**

required

Give this to the app as the place it reports removals.

string format: uri

**createdAt**

required

string format: date-time

**updatedAt**

required

string format: date-time

**removedAt**

string format: date-time

##### Examplegenerated

```json
{
  "appId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
  "app": "example",
  "label": "example",
  "oauth2": {
    "clientId": "example",
    "clientSecret": {
      "envName": "example",
      "name": "example"
    },
    "tenant": "example"
  },
  "githubApp": {
    "appNumber": 1,
    "slug": "example",
    "clientId": "example",
    "clientSecret": {
      "envName": "example",
      "name": "example"
    },
    "privateKey": {
      "envName": "example",
      "name": "example"
    },
    "webhookSecret": {
      "envName": "example",
      "name": "example"
    }
  },
  "projects": [
    "example"
  ],
  "redirectUrl": "https://example.com",
  "noticesUrl": "https://example.com",
  "createdAt": "2026-04-15T12:00:00Z",
  "updatedAt": "2026-04-15T12:00:00Z",
  "removedAt": "2026-04-15T12:00:00Z"
}
```

### 400

Malformed body or unknown field (`bad-input`, `details.field`); a registration that doesn’t fit its entry, or that would serve a project another registration of the app serves (`connection-app-invalid`, `details.field`); no such app (`connection-app-unknown-entry`); a model provider’s key named as a secret (`provider-key-refused`); or a secret under the reserved `kindgi.` prefix (`reserved-name-prefix`).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 401

Missing / malformed / expired / revoked bearer token.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 403

Not a tenant admin (`permission-denied`).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 409

Idempotency-Key was reused with a different body, or resource-state conflict. Or `env-not-served`: this runtime keeps every value under the env it serves (whoami’s `servedEnv`, with `refusesOtherEnvs`), and the secret reference names another. Name it under `details.servedEnv`. Nothing is stored.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 413

The request body is over the API’s limit (`request-too-large`; `details.maxBytes` says how much).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 500

Server error (unmapped domain code or framework crash).

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

### 503

The `Idempotency-Key` store couldn’t be read before the request ran (`idempotency-store-unavailable`), so nothing was changed; `Retry-After` (whole seconds) says when to try again with the same key.

application/json

object

**error**

required

object

**code**

required

Stable machine-readable discriminant. Values match domain error codes (see `docs/API-ROUTE-CONVENTIONS.md` §4.3).

string

**message**

required

string

**details**

Optional, kind-specific.

object

**_key_**

additional properties

any

**requestId**

required

Server-assigned request id; also echoed via `X-Request-Id` header.

string

##### Examplegenerated

```json
{
  "error": {
    "code": "example",
    "message": "example",
    "details": {},
    "requestId": "example"
  }
}
```

## See also

- [`GET /v1/connection-apps`: List the tenant's app registrations](https://docs.kindgi.com/v0.4.0/reference/api/operations/connectionappslist.md)
- [Error code `bad-input`](https://docs.kindgi.com/v0.4.0/reference/errors.md#bad-input)
- [Error code `connection-app-invalid`](https://docs.kindgi.com/v0.4.0/reference/errors.md#connection-app-invalid)
- [Error code `connection-app-unknown-entry`](https://docs.kindgi.com/v0.4.0/reference/errors.md#connection-app-unknown-entry)
- [Error code `env-not-served`](https://docs.kindgi.com/v0.4.0/reference/errors.md#env-not-served)
- [Error code `idempotency-store-unavailable`](https://docs.kindgi.com/v0.4.0/reference/errors.md#idempotency-store-unavailable)
- [Error code `permission-denied`](https://docs.kindgi.com/v0.4.0/reference/errors.md#permission-denied)
- [Error code `provider-key-refused`](https://docs.kindgi.com/v0.4.0/reference/errors.md#provider-key-refused)
- [Error code `request-too-large`](https://docs.kindgi.com/v0.4.0/reference/errors.md#request-too-large)
- [Error code `reserved-name-prefix`](https://docs.kindgi.com/v0.4.0/reference/errors.md#reserved-name-prefix)
- [Every error code](https://docs.kindgi.com/v0.4.0/reference/errors.md)
