> For the complete documentation index, see [llms.txt](https://docs.nullify.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nullify.ai/api-reference/api-reference/api-admin/integrations-source-control-and-ci.md).

# Source Control and CI Integrations

These operations list the source-control installations your token can reach, manage the Azure DevOps webhook and Boards configuration, and store credentials for the Buildkite, CircleCI and Jenkins CI integrations. Each operation lists the permission it requires: `config:read`, `config:write`, or for one Azure read, `secret-config:read`. See [Authentication](/api-reference/api-reference/authentication.md) for tokens.

Provider scope: Azure operations that act on an organization take it in the request body or in the `azureOrganizationId` query parameter, as noted per operation. Use [List installations](#list-installations) to discover the right scope value for each installation. For the user-facing Azure DevOps setup, see the [Azure DevOps installation guide](/enterprise-tier/azure-install.md).

## Installations

### List installations

Returns the app installations your caller identity can access across GitHub, Azure DevOps, Bitbucket and GitLab. Each entry carries a `provider` object, a display name, an avatar URL where the provider has one, `queryParameters` (for example `githubOwnerId` or `azureOrganizationId`) and, when it can be resolved, `nullifyBaseUrl`. Pass the `queryParameters` values as the provider scope on other admin calls and send later requests to `nullifyBaseUrl` when it is set. This endpoint has no permission requirement beyond a valid identity.

```bash
curl -s \
  -H "Accept: application/json" \
  -H "Authorization: Bearer <TOKEN>" \
  'https://api.<TENANT>.nullify.ai/admin/installations'
```

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/installations" method="get" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

## GitHub

### Get GitHub App settings

Reads the webhook events and permissions currently granted to the GitHub App installation for the scoped owner (`githubOwnerId`) and compares them with the set Nullify expects. The response lists `events`, `permissions`, `missingEvents`, `missingPermissions` and `incorrectPermissions`, so you can spot an installation that needs its permissions re-approved. Failures to resolve the installation do not return an HTTP error: the response carries an `error` message instead (for example when the scope is not a GitHub owner or has no installation ID). Requires `config:read`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/github/app-settings" method="get" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Discover GitHub Packages identity

Takes a classic GitHub personal access token in the request body and returns the token's `username`, `packageOrigin` and the npm packages it can read, so a connect form can prefill fields before any connection is created. Nothing is stored. The scope must be a GitHub owner (`githubOwnerId`), because discovery runs against that owner; a rejected token returns 400 and an upstream failure returns a retryable 5xx. The token is never echoed back. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/github-packages/discover" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

## Azure DevOps

Azure DevOps has two layers. The git connector (organizations, repositories, service hooks) is connected through the consent flow described in the [installation guide](/enterprise-tier/azure-install.md), and the operations below manage the webhooks and policies that connector uses. A separate Boards credential, stored by [Set Azure Boards credentials](#set-azure-boards-credentials), enables ticket creation in Azure DevOps Boards.

### Install service hooks

Provisions Azure DevOps service hooks for every repository the connected credentials can see. Scope it with `azureOrganizationId` (or the organization headers your identity carries). Subscriptions that are disabled or have drifted are replaced in place, and subscriptions already delivering to Nullify are skipped unless you pass `reconcile=true`, which rewrites them with the current webhook URL and per-install secret. The response reports `hooksCreated`, `hooksReplaced`, `hooksSkipped` and a `failures` list rather than failing the whole call, so check `failures` before assuming every repository is covered. Returns 400 for an organization that is not one of the caller's, and 403 for one that has no verified connection. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/azure" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### List webhook installations

Lists the Azure DevOps webhook installations registered for your tenant, one per organization and project, with each install's creation and update times, whether it has been suspended (`adminDisabledAt`, `adminDisabledBy`) and `webhookSecretRotatable`. The per-install webhook secret is never returned here. Requires `config:read`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/azure/installations" method="get" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Register a webhook installation

Registers, or rotates the secret of, the webhook installation for one Azure DevOps project. Send `organizationId` and `projectId` in the JSON body. The `organizationId` here is the account id that webhook deliveries carry as `resourceContainers.account.id`, which is not the collection id used by the Azure DevOps REST API and not the `azureOrganizationId` query parameter; a row registered with the wrong id never matches a delivery. The response returns the shared `webhookSecret` once, so store it immediately. Returns 403 if the organization is not connected or the install belongs to another tenant, and 412 if the organization's secret is managed by Nullify's router (reconnect the organization to rotate it) or was disabled by Nullify. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/azure/installations" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Delete a webhook installation

Removes the install for the `organizationId` and `projectId` query parameters (both required). Before deleting the row, Nullify tries to remove the project's service-hook subscriptions and its branch policy. That cleanup is best-effort: anything that could not be removed is listed in `failures`, and the row is deleted regardless, so remove leftovers by hand in Azure DevOps if `failures` is not empty. Returns 404 if the install does not exist. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/azure/installations" method="delete" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Suspend or resume an organization

Pass `organizationId` and `suspended` in the JSON body. With `suspended: true` Nullify disables the organization's service-hook subscriptions, makes its branch policy non-blocking and marks each install row disabled; `suspended: false` reverses all three. `installationsAffected` counts Nullify install rows only, so read `failures` to learn whether Azure DevOps itself stopped or resumed delivering: a non-empty list means the organization may still be sending events. Resuming an organization that Nullify itself disabled is refused with 412. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/azure/installations/suspend" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Test an organization

A read-only recheck of an already-connected organization. It verifies connectivity, lists repositories and reports missing, drifted or disabled service-hook subscriptions as a `checklist`, with `ok` false when any item fails. It creates, replaces and disables nothing. Send `organizationId` in the JSON body. Returns 404 for an unknown organization. Requires `config:read`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/azure/installations/test" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Preview CODEOWNERS as required reviewers

Reads one repository's CODEOWNERS file and reports the Azure DevOps required-reviewer policies it would translate to, plus the owners and rules that could not be carried over. Send `organizationId`, `projectId` and `repositoryId` in the JSON body. `path` must be `CODEOWNERS`, `.github/CODEOWNERS` or `docs/CODEOWNERS` (any other value returns 400); omit it to try those three in order. `ref` selects the branch and defaults to the repository's default branch. Read-only: no policy is created, changed or deleted. Requires `config:read`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/azure/codeowners/preview" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Get Azure Boards configuration

Returns the Azure client ID and Entra tenant ID used for Azure DevOps Boards ticketing, whether a client secret has been stored (`clientSecretUploaded`) and a redacted form of it. When Boards settings were saved, the response also includes the organization, project name and work item type. The secret itself is never returned. This is the only operation in this page that requires `secret-config:read` rather than `config:read`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/azure" method="get" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Set Azure Boards credentials

Stores the client ID, tenant ID and client secret (all required in the JSON body) used to create tickets in Azure DevOps Boards. The secret is encrypted and stored for your tenant. If you also send `projectName`, Nullify first checks that the `organization` is connected to your workspace and that the project's process defines the work item type (or a default one) with states it can use to close and reopen items; a failed check returns 400 and nothing is saved. Saving a `projectName` also enables automatic ticket creation for Azure DevOps by default. The response echoes the configuration with the secret redacted. Requires `config:write`. See [ticketing.md](/api-reference/api-reference/api-admin/ticketing.md) for how tickets are used.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/azure/credentials" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

## Buildkite

### Get Buildkite status

Returns `configured` and, when it is, the stored `orgSlug`. Configured means an organization slug is stored; the token is never returned. Requires `config:read`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/buildkite" method="get" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### List Buildkite organizations for a token

Takes a Buildkite API token in the body and returns the organizations (`slug` and `name`) it can access, so you can choose a slug before saving. The token must start with `bkua_` followed by alphanumeric characters, otherwise the call returns 400, as it does when Buildkite rejects the token. Nothing is stored. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/buildkite/organizations" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Set the Buildkite token

Verifies the token against Buildkite, then stores it with the organization slug. `apiToken` is required and must start with `bkua_`. `orgSlug` is optional: if you omit it Nullify selects the organization the token can access, and it needs you to supply one when the token sees more than one. A slug must be alphanumeric with hyphens. The stored slug uses the casing Buildkite reports and is returned as `orgSlug`. Bad credentials return 400, upstream failures a retryable 5xx. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/buildkite/token" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Delete the Buildkite integration

Removes the stored Buildkite token and organization slug. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/buildkite" method="delete" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

## CircleCI

### Get CircleCI status

Returns whether a CircleCI token is stored (`configured`). Requires `config:read`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/circleci" method="get" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Set the CircleCI token

Stores the CircleCI API token. `apiToken` is required in the body; unlike Buildkite, the token is not verified against CircleCI before it is saved. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/circleci/token" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Delete the CircleCI integration

Removes the stored CircleCI token. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/circleci" method="delete" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

## Jenkins

### Get Jenkins status

Returns whether a Jenkins integration is configured (`configured`, true when a Jenkins URL is stored). Requires `config:read`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/jenkins" method="get" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Set the Jenkins token

Stores the Jenkins URL, username and API token; all three are required in the body. The URL must be a valid `http` or `https` URL and must not point at loopback, link-local or private addresses (including hostnames that resolve to them), otherwise the call returns 400. The credentials are not tested against Jenkins before they are saved. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/jenkins/token" method="post" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}

### Delete the Jenkins integration

Removes the stored Jenkins URL, username and API token. Requires `config:write`.

{% openapi src="/files/1NgvLK12PRq7RFdn97yd" path="/admin/integrations/jenkins" method="delete" %}
[nullify-openapi-bundle.yaml](https://3946391995-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FId2OtLCL6O6V3XSRYxtr%2Fuploads%2Fgit-blob-a96d8165240befab9d431c87b16a7387b6914215%2Fnullify-openapi-bundle.yaml?alt=media)
{% endopenapi %}
