---
title: "Data model"
description: "How refs, threads, notifications and events describe the same things on every forge."
canonical_url: "https://docs-oquac2tli-poetry.vercel.app/concepts/data-model"
---
# Data model

> How refs, threads, notifications and events describe the same things on every forge.

Forges name and number things in different ways. `forges` describes them with a small set of shared types, so code that handles a GitHub issue handles a Forgejo issue too.

## Refs

A ref points at a resource. It is a plain object that you can build by hand, store and send between processes. Every ref has `forge` and `instance`:

- A `RepoRef` adds `owner` and `name`. On GitLab, `owner` can contain slashes for nested groups, and `name` never does.
- A `ThreadRef` adds the `repo`, a `kind` and a `number`.
- Comments, releases, webhooks, checks and notifications have refs of their own.

```ts
const repo = {
  forge: 'gitlab',
  instance: 'gitlab.com',
  owner: 'acme/platform',
  name: 'widgets',
} as const
const ref = {
  forge: 'gitlab',
  instance: 'gitlab.com',
  repo,
  kind: 'pull_request',
  number: '23',
} as const
```

`forge` names the kind of forge, such as `'github'`. `instance` is the host of one deployment, such as `'github.com'` or `'ghe.example.com'`. Two providers of the same forge differ by instance.

### Thread kinds

A thread is anything with a conversation. `kind` is one of `issue`, `pull_request`, `discussion`, `commit` or `other`. A GitLab merge request has `kind: 'pull_request'`. A subject that has no shared kind, such as a check suite, is `other`, and `typeRaw` names it.

### Keys

Use a key when you need a map key or a database column. `repoKey()`, `threadKey()` and `notificationKey()` return a stable string. `parseRepoKey()`, `parseThreadKey()` and `parseNotificationKey()` read it back. A repository key uses the forge's own ID when there is one, so a rename or transfer keeps the key.

`repoSlug()` and `threadSlug()` return the form people type, such as `acme/widgets#42`.

## Normalised and raw values

A `Thread` has a `state` of `open`, `closed`, `merged` or `unknown`. It also has `labels`, `assignees`, `author` and timestamps as `Date` objects. A pull request also has `branches`, `checks` and `stack`.

Where a forge has a value that the model can't express, the model keeps both sides. A thread's `state` has a matching `stateRaw`, a notification's `reason` has `reasonRaw`, and an event's `action` has `actionRaw`. The `raw` field holds the unnormalised forge payload. Use the raw fields when you need forge-specific detail, and the normalised ones everywhere else.

## Optional fields

A field is optional when some forges don't report it. On a forge that doesn't return a comment count, `thread.commentCount` is `undefined` rather than `0`, so check for `undefined` before you use an optional field.

## Warnings

A result can carry `warnings`. A warning describes a problem that didn't fail the call, such as a filter the forge ignored. See [Handle errors and rate limits](https://docs-oquac2tli-poetry.vercel.app/guides/errors#warnings).

## Next steps

- [Capabilities](https://docs-oquac2tli-poetry.vercel.app/concepts/capabilities) explains how support differs by forge.
- [Events](https://docs-oquac2tli-poetry.vercel.app/concepts/events) describes webhook deliveries and live streams.
- The [provider API](https://docs-oquac2tli-poetry.vercel.app/reference/provider-api) lists every namespace and method.


## Sitemap

See the full [sitemap](https://docs-oquac2tli-poetry.vercel.app/sitemap.md) for all pages.
