# CI trust rules

> The repositories whose GitHub Actions or GitLab CI jobs may post annotations with their OIDC token, without a stored secret.

A trust rule lets a repository's CI jobs post annotations with the OIDC token that their provider gives each job, in place of an ingest key. To set one up end to end, follow [Post from CI without a secret](https://telemetrymachine.com/docs/annotations#post-from-ci-without-a-secret).

- [`POST /`](https://telemetrymachine.com/docs/api/ci-trust-rules#create-a-trust-rule) Create
- [`GET /`](https://telemetrymachine.com/docs/api/ci-trust-rules#list-trust-rules) List
- [`DELETE /{public_id}`](https://telemetrymachine.com/docs/api/ci-trust-rules#delete-a-trust-rule) Delete

Anyone in the organization may list the rules. Owners and admins create and delete them, with a personal access token, or with an OAuth sign-in that has `organization:manage`.

Rules work with github.com and gitlab.com. GitHub Enterprise Server and self-managed GitLab aren't supported yet.

## The trust rule object

**Attributes**

- `public_id` string _server_

  `ctr_…`

- `provider` enum _required_

  One of `github`, `gitlab`

- `repository` string _required_

  `owner/repo` on GitHub, or the project's path on GitLab, such as `acme/platform/checkout`.

- `repository_id` string, nullable _optional_

  The provider's id for the repository. Left out, the first job that the rule lets in sets it. From then on the rule matches the id, not the name, so a renamed or recreated repository doesn't inherit the trust.

  Rules: digits only.

- `ref` string, nullable _optional_

  Only jobs on this ref, such as `refs/heads/main` or `refs/tags/v1.42.0`. Null for any ref.

  Rules: a full ref, starting `refs/heads/` or `refs/tags/`.

- `environment` string, nullable _optional_

  Only jobs in this deployment environment, such as `production`. Null for any environment, or none.

  Rules: not blank.

- `created_by` object, nullable _server_

  The person who created it.

- `created_at` timestamp _server_

  When it was created.

## Which jobs a rule lets in

A job's token gets in when it is valid and a rule matches it:

1. The token's signature checks out against the provider's published keys.
2. It was issued by the provider, it is addressed to `telemetrymachine.com`, and it hasn't expired. Otherwise the call gets 401.
3. A rule of the organization in the URL names the job's repository, by id once the rule has one, and the job's ref and environment, where the rule names them. Otherwise the call gets 403.

A job must match everything that a rule names. To let in the jobs on `main` and also the jobs in `production`, create two rules. A GitLab branch such as `main` is matched as `refs/heads/main`.

A job that gets in may create annotations and retry its own keys. It can't update or delete annotations.

## Create a trust rule

`POST /api/organizations/{org}/ci-trust-rules`

- Auth: An owner or admin
- Returns: [The trust rule object](https://telemetrymachine.com/docs/api/ci-trust-rules#the-trust-rule-object)

Takes the fields of [the trust rule object](https://telemetrymachine.com/docs/api/ci-trust-rules#the-trust-rule-object) that are marked required or optional.

```sh tab="curl" title="POST /ci-trust-rules"
curl -X POST https://telemetrymachine.com/api/organizations/<your-org>/ci-trust-rules \
  -H "Authorization: Bearer $TM_TOKEN" -H 'Content-Type: application/json' \
  -d '{ "provider": "github", "repository": "acme/checkout", "ref": "refs/heads/main" }'
```

```http tab="HTTP" title="POST /ci-trust-rules"
POST /api/organizations/<your-org>/ci-trust-rules
Authorization: Bearer TOKEN
Content-Type: application/json

{ "provider": "github", "repository": "acme/checkout", "ref": "refs/heads/main" }
```

```json status="200" title="Response"
{
  "status": 200,
  "data": {
    "public_id": "ctr_01m5z3h7q2w8e4r6t1y9u0i5op",
    "provider": "github",
    "repository": "acme/checkout",
    "repository_id": null,
    "ref": "refs/heads/main",
    "environment": null,
    "created_by": { "display_name": "Maya", "email": "maya@acme.dev" },
    "created_at": "2026-10-26T16:20:00.000Z"
  }
}
```

## List trust rules

`GET /api/organizations/{org}/ci-trust-rules`

- Auth: Any member
- Returns: `rules`, oldest first

## Delete a trust rule

`DELETE /api/organizations/{org}/ci-trust-rules/{public_id}`

- Auth: An owner or admin
- Returns: `{ "deleted": true }`

Jobs that no other rule lets in can't post from then on. The annotations that they posted stay.

## Errors

| Status | When                                                                          | What to do                                        |
| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------- |
| 400    | A field breaks its rules, such as a `ref` that isn't a full ref               | Fix the field that the error names.               |
| 403    | You aren't an owner or admin, or an OAuth sign-in lacks `organization:manage` | Ask an owner or admin, or sign in with the scope. |
| 404    | The organization has no rule with this `public_id`                            | Check the id with [List](https://telemetrymachine.com/docs/api/ci-trust-rules#list-trust-rules).      |

---

All pages: https://telemetrymachine.com/docs/llms.txt
