<div align='center'>
  <img src='https://i.imgur.com/ZzjmE8i.png' />
</div>

<br />

<div align='center'>
  <a href='https://github.com/puregram/puregram'><b><code>puregram</code></b></a>
  <span>&nbsp;•&nbsp;</span>
  <a href='#whats-inside'><b>what's inside</b></a>
  <span>&nbsp;•&nbsp;</span>
  <a href='#types-only-subpath'><b>types-only subpath</b></a>
  <span>&nbsp;•&nbsp;</span>
  <a href='https://t.me/pureforum'><b>telegram forum</b></a>
</div>

## @puregram/api

_autogenerated bot api types, structures, update classes, and factories for `puregram` package_

### introduction

**you usually don't install this directly** — `puregram` already depends on it and re-exports everything you'll typically need (`TelegramMessage`, `MessageUpdate`, `Chat`, `User`, factories, the works). install `@puregram/api` separately when you're:

- writing a plugin that touches the wrapped-update surface (your plugin lives outside `puregram` core but still needs `MessageUpdate`, `CallbackQueryUpdate`, etc)
- shipping a **types-only** consumer — a separate service, a frontend, a mock generator — and want zero runtime dep
- building tooling on top of the schema (test fixture generators, openapi adapters, …)

everything in `@puregram/api` is **deterministically codegen'd** from the bot api schema (`corefork.telegram.org/bots/api` + `core.telegram.org/bots/api`) by `scripts/parse.ts` + `scripts/emit.ts`. the schema checkpoint is committed at `schema/<bot-api-version>.json` per release, so generated output is reproducible offline

### versioning

`@puregram/api`'s version mirrors the bot api version: `<bot_api_major>.<bot_api_minor>.<puregram_patch>`. example: `10.0.3` = bot api 10.0, third puregram-side iteration on top.

> **pin with `~`, not `^`.** telegram's "minor" bumps regularly rename fields, retype shortcuts, or remove deprecated objects — npm's `^10.0.0` would auto-upgrade across them and break your build. use `~10.0.0` (patch-only) or an exact pin like `10.0.3`. other `@puregram/*` packages follow regular semver; core remains on `3.x` and may include explicitly announced breaking changes in minor releases.

**10.3.3 changes existing update-shortcut calling conventions.** use `inlineQuery.answer(results, params?)` and `preCheckoutQuery.answer(ok, params?)` instead of the old all-object shortcut calls. raw `tg.api.*` calls are unchanged. SDK patch iterations can contain breaking shortcut changes, so use an exact version or a lockfile when you need to defer migration. see the [3.10.0 / 10.3.3 migration guide](https://github.com/puregram/puregram/blob/v3/docs/guide/concepts/shortcuts.md#migrating-to-puregram-3100--api-1033), including the impact on older core installations with a `~10.3.x` API dependency.

new bot api versions are picked up by an automated watcher workflow that opens a PR with the regenerated schema + codegen — review the diff before merging, since handcrafted code occasionally needs touch-ups for breaking telegram changes

### example

a tiny plugin that logs the chat type of every incoming message:

```ts
import {
  ChatType,
  defineFilter,
  type AnyUpdate,
  type MessageUpdate
} from '@puregram/api'
import { createPlugin } from 'puregram'

const isPrivateMessage = defineFilter<MessageUpdate, { chat: { type: 'private' } }>(
  'isPrivateMessage',
  (update) => update.is('message') && update.chat.type === ChatType.Private,
  { kinds: ['message'] }
)

export function chatLogger () {
  return createPlugin({
    name: 'chatLogger',
    install: (telegram) => {
      telegram.use((update: AnyUpdate, next) => {
        if (update.is('message')) {
          console.log(update.chat.type, update.text)
        }

        return next()
      })

      return {}
    }
  })
}
```

### installation

```sh
$ yarn add @puregram/api
$ npm i -S @puregram/api
```

requires node `>=22.0.0`

---

<a name='whats-inside'></a>
## what's inside

every export below comes either straight from codegen (✦) or from a small handcrafted helper (no marker) that the codegen leans on:

### bot api types ✦

every `TelegramX` interface from the bot api spec — `TelegramMessage`, `TelegramUser`, `TelegramChat`, `TelegramCallbackQuery`, `TelegramInlineQuery`, …

```ts
import type { TelegramMessage, TelegramUser } from '@puregram/api'

function describe (raw: TelegramMessage): string {
  return `${raw.chat.id}: ${raw.text ?? '<no text>'}`
}

function rename (user: TelegramUser): string {
  return `${user.first_name} ${user.last_name ?? ''}`.trim()
}
```

### method param types ✦

every method's params interface — `SendMessageParams`, `EditMessageTextParams`, `AnswerCallbackQueryParams`, …. plus the unified `ApiMethods` mapping (`{ sendMessage: (params) => Promise<TelegramMessage>, … }`) which `puregram` wraps via the `tg.api.X` proxy

### enums ✦

string-literal enums for the bot-api fields that have closed sets of values:

```ts
import { ChatType, MessageEntityType } from '@puregram/api'

ChatType.Private          // 'private'
ChatType.Group            // 'group'
ChatType.Supergroup       // 'supergroup'
ChatType.Channel          // 'channel'

MessageEntityType.Mention      // 'mention'
MessageEntityType.TextMention  // 'text_mention'
MessageEntityType.Code         // 'code'
// …
```

(`AttachmentType`, `ChatType`, `MessageEntityType`, `PollType`, `StickerType` — the curated set the bot api actually closes over)

### structure wrapper classes

class wrappers around frequently-accessed objects — lazy + memoized getters, `raw` always exposed, custom inspect:

```ts
import { Chat, Message, User } from '@puregram/api'

const m = Message.fromPayload(rawTelegramMessage)

m.id              // number
m.text            // string | undefined
m.from            // User | undefined (lazy-wrapped)
m.chat            // Chat (lazy-wrapped)
m.raw             // the raw TelegramMessage payload
```

every structure has a `static fromPayload(raw)` factory and exposes `.raw` as escape hatch. nested-object getters are `??=`-cached so wrapper allocation is one-shot per access path. **the same classes are re-exported from `puregram`** — for most consumers, the `puregram` re-export is the right entry point

a few `T[]` fields are handcrafted **collection wrappers** rather than plain arrays — iterable, array on `.raw`, plus the query the array made awkward:

```ts
message.photo?.biggest          // Photo    — also .smallest, .byMin(width)
video.qualities?.byCodec('av01') // VideoQualities
update.added.emojis             // Reactions — also .customEmojiIds, .has, .hasPaid()
update.reactions.total          // ReactionCounts — also .top, .countOf(emoji)
poll.options.winner             // PollOptions — also .totalVotes, .byId(id)
```

### update classes ✦

one class per bot-api update kind plus per derived service-event. `MessageUpdate`, `CallbackQueryUpdate`, `EditedMessageUpdate`, `InlineQueryUpdate`, `ChatMemberUpdate`, `NewChatMembersUpdate` (service event), …. every class is a discriminated subclass of the `AnyUpdate` union, with:

- `kind` — literal-typed string discriminant (`'message'`, `'callback_query'`, …)
- `raw` — the bot-api payload as-is
- `is(kind)` — type predicate (`update.is('message')` narrows to `MessageUpdate`)
- `hasX()` — codegen'd type predicates for optional fields (`update.hasText()`, `update.hasPhoto()`, …)
- shortcut methods — `update.send(...)`, `update.edit(...)`, `update.delete()`, `update.answer(...)` etc, with `chat_id`/`message_id`/`callback_query_id` auto-filled from the payload
- a custom `Symbol.for('nodejs.util.inspect.custom')` for readable `console.log` output

```ts
import type { MessageUpdate } from '@puregram/api'

function summarize (update: MessageUpdate): string {
  return `from ${update.from?.firstName ?? '?'}: ${update.text ?? ''}`
}
```

### `AnyUpdate` discriminated union

the type of an arbitrary wrapped update — every bot-api update class plus `CustomUpdate` (for plugin-emitted custom kinds). narrowed via `update.is(kind)`:

```ts
import type { AnyUpdate, CallbackQueryUpdate, MessageUpdate } from '@puregram/api'

function dispatch (update: AnyUpdate) {
  if (update.is('message')) {
    const m: MessageUpdate = update
    return m.send('echo')
  }

  if (update.is('callback_query')) {
    const q: CallbackQueryUpdate = update
    return q.answer({ text: 'hi' })
  }
}
```

### `tg.X` shortcut method types ✦

`TelegramShortcuts` — the interface `puregram` core implements for the `tg.send(chat, text)` / `tg.sendPhoto(chat, photo)` / `tg.forward(...)` / etc convenience layer. one entry per shortcut, each with positional first args + optional params object

### service-event derivation map ✦

`SERVICE_EVENT_MAP` and `ServiceActionKind` — the dispatcher's runtime table for "if a `TelegramMessage` payload has `new_chat_members` set, dispatch as `NewChatMembersUpdate` instead of `MessageUpdate`". rarely touched by user code, but exported for plugin authors who want to do the same derivation

### factories ✦

codegen'd `InputMedia`, `InputPaidMedia`, `InputProfilePhoto`, `InputStoryContent`, `InlineQueryResult`, `InlineQueryResultCached` static-factory classes:

```ts
import { InputMedia } from '@puregram/api'

const arr = [
  InputMedia.photo({ media: 'attach://a.jpg', caption: 'one' }),
  InputMedia.video({ media: 'attach://b.mp4' })
]
```

**heads up**: these are the **bot-api-shaped** codegen versions. `puregram` shadows `InputMedia`, `InlineQueryResult`, and `InputMessageContent` with handcrafted classes that have nicer ergonomics — positional `media` arg on `InputMedia.X(...)`, renamed `content` / `replyMarkup` / `thumbnail` fields on `InlineQueryResult.X(...)`. for application code, **import them from `puregram`, not from `@puregram/api`** — the puregram-shadowed versions accept everything the codegen versions do plus more

### filter runtime + codegen'd filter families

```ts
import { defineFilter, defineAsyncFilter, and, or, not, every, some, isFilter } from '@puregram/api'
import type { Filter, AsyncFilter, FilterMeta, FilterMethods } from '@puregram/api'

const isPrivateMessage = defineFilter<MessageUpdate, { chat: { type: 'private' } }>(
  'isPrivateMessage',
  (update) => update.is('message') && update.chat.type === 'private',
  { kinds: ['message'] }
)

if (isFilter(isPrivateMessage)) {
  console.log(isPrivateMessage.name)
}
```

`@puregram/api` provides the **runtime + codegen'd presence/kind filter families** (`hasText`, `hasPhoto`, …, `kind.message`, `action.newChatMembers`, …). the parametric/parsing filters (`command`, `regex`, `chatId`, `from`, `callbackData`, …) are handcrafted in `puregram` core. typically you import via `puregram`'s `filters` namespace — `@puregram/api` is the source for plugin authors who want to define filters that don't depend on `puregram` core

### custom updates

```ts
import { CustomUpdate, CustomUpdateRegistry } from '@puregram/api'

const u = new CustomUpdate('job_done', { jobId: 'x' })

u.kind          // 'job_done'
u.payload       // { jobId: 'x' }
```

these are the runtime+type primitives behind `tg.defineUpdate(name)` + `tg.emit(name, payload)`. user code rarely instantiates `CustomUpdate` directly

### dispatch types

```ts
import type { UpdateHandler, OnOptions, Priority } from '@puregram/api'

const handler: UpdateHandler<MessageUpdate> = async (m, next) => {
  await m.send('ok')
  await next()
}

const opts: OnOptions = { priority: 'high' as Priority }
```

`puregram` core consumes these to type `tg.onMessage(handler, options?)` and friends

### `Has<T, K>` and `Modify<T, M>`

narrowing helpers used throughout the codegen for `update.hasText()` predicates and filter-attached `match` properties:

```ts
import type { Has, Modify, MessageUpdate } from '@puregram/api'

// MessageUpdate where `text` is required
type WithText = Has<MessageUpdate, 'text'>

// MessageUpdate enriched with a regex match
type WithMatch = Modify<MessageUpdate, { match: RegExpMatchArray }>
```

### `Formattable`

structural `{ text: string, entities?: readonly TelegramMessageEntity[] }` shape. method param fields like `text` / `caption` accept this in addition to plain `string`, which is what makes `@puregram/markup`'s tagged-template builders work without any `parse_mode` setup

### `TelegramLike`

structural interface every `Telegram` instance satisfies, exposed without a circular dep on `puregram` core. plugins that don't need the full `Telegram<Ext>` type can take `TelegramLike` and stay loose. beyond `api` and the download / chat-action helpers it carries `bot: TelegramUser` — the bot's own identity, resolved via `getMe` (or the `bot` option) before dispatch starts, so `update.tg.bot` is always populated inside handlers

### `FORMATTABLE_FIELDS` and `WEBHOOK_REPLY_SAFE_METHODS`

two codegen'd lookup maps:

- `FORMATTABLE_FIELDS` — for each method, which params accept `Formattable` (`text`, `caption`, `question`, …). consumed by `@puregram/markup` to know which fields to unwrap
- `WEBHOOK_REPLY_SAFE_METHODS` — methods that telegram lets you reply with directly to a webhook POST (zero-rtt path). consumed by `puregram`'s webhook transport

```ts
import { FORMATTABLE_FIELDS, WEBHOOK_REPLY_SAFE_METHODS } from '@puregram/api'

FORMATTABLE_FIELDS.sendMessage           // ['text']
FORMATTABLE_FIELDS.sendPhoto             // ['caption']
WEBHOOK_REPLY_SAFE_METHODS.has('sendMessage')  // true
```

---

<a name='types-only-subpath'></a>
## the `@puregram/api/types` subpath

if you want **zero runtime** — just the bot-api `TelegramX` interfaces, none of the wrapper classes or factories — import from the `/types` subpath:

```ts
import type { TelegramMessage, TelegramUser } from '@puregram/api/types'
```

resolves to `lib/generated/types.d.ts` only. the rest of the package isn't loaded. perfect for type-only consumers — frontends sharing types with the bot, fixture generators, mock libraries, anywhere the runtime would just be dead weight

⚠ **known caveat (tracked for v3 cleanup)**: `@typescript-eslint/parser`'s type-checked rule set doesn't always honor the `types` condition on the exports map, so imports through `@puregram/api/types` can come back as `any` to ts-eslint. workaround: import from the root `@puregram/api` instead — the root entry re-exports everything anyway

---

## the schema checkpoint

every release commits a `schema/<bot-api-version>.json` snapshot — the parser's deterministic output:

```
@puregram/api/schema/
├── 10.0.json          # current — the only checkpoint shipped to npm
└── archive/
    └── 9.6.json       # superseded checkpoints, kept in git history
```

only the current `schema/<bot-api-version>.json` ships in the npm tarball — `files` globs `schema/*.json`, which doesn't reach `archive/`. superseded checkpoints stay in git so schema diffs remain reviewable. the JSON file is canonical and reviewable. diffing schema PRs surfaces what bot-api actually changed before the codegen output does. emit is reproducible offline:

```sh
$ yarn parse        # rescrape corefork + core, write schema/<latest>.json (shelving the previous one)
$ yarn emit         # codegen src/generated/*.ts from the committed schema
$ yarn verify       # CI check — fails if generated/ drifts from emit's output
$ yarn regenerate   # parse + emit in one go
```

the package `version`'s `major.minor` records the bot-api version this checkpoint targets (e.g. `10.0.x` → bot api 10.0); the trailing patch is puregram's own iteration counter

---

## bot-api version policy

- **one bot-api version per puregram release.** no multiplexing — the codegen + the runtime + the published types all line up against one schema
- **minor bot-api bump** → puregram minor
- **breaking bot-api change** → puregram major

if telegram bumps the format faster than `@puregram/api` updates, the parser scripts in `scripts/` are designed to be runnable offline against a downloaded html snapshot — see `scripts/parse.ts` and `scripts/emit.ts`. open an issue or PR with the new schema if you need it sooner

---

## exported surfaces — quick reference

```ts
// types
import type {
  TelegramMessage, TelegramUser, TelegramChat,            // …all bot-api objects
  SendMessageParams, ApiMethods,                          // method param shapes + the proxy interface
  TelegramShortcuts,                                      // tg.send / tg.sendPhoto / … signatures
  TelegramLike,                                           // structural Telegram shape
  AnyUpdate,                                              // wrapped update union (bot-api + custom)
  Has, Modify,                                            // narrowing helpers
  UpdateHandler, OnOptions, Priority,                     // dispatch types
  Filter, AsyncFilter, FilterMeta, FilterMethods,         // filter typing
  Formattable                                             // text/caption rich shape
} from '@puregram/api'

// runtime
import {
  Chat, Message, User, /* …all wrapper classes */         // structures
  MessageUpdate, CallbackQueryUpdate, /* …all updates */   // update classes
  ChatType, MessageEntityType, /* …all enums */            // enum members
  InputMedia, InlineQueryResult, /* …all factories */      // codegen factories (puregram shadows these — prefer puregram's import for app code)
  defineFilter, defineAsyncFilter, isFilter, and, or, not, every, some,
  CustomUpdate, CustomUpdateRegistry,
  FORMATTABLE_FIELDS, WEBHOOK_REPLY_SAFE_METHODS
} from '@puregram/api'

// types-only zero-runtime subpath
import type { TelegramMessage } from '@puregram/api/types'
```
