Advanced

Raw API & schema layer

When a helper does not exist for what you want, drop down to the raw TL method. Every constructor and method in the schema has a Go binding, generated and kept in lockstep with the upstream layer.

What raw API is

Telegram's API is described in a TL (Type Language) schema: a flat list of constructors (data shapes) and methods (RPCs). gogram parses the schema and generates a Go type for every constructor and a method on *Client for every RPC, named in UpperCamelCase. messages.sendMessage becomes MessagesSendMessage, channels.getMessages becomes ChannelsGetMessages, and so on for ~2 400 entries.

Helpers like SendMessage and SendMedia are thin wrappers over those. When you need fine-grained control, or when a method does not have a helper yet, you call the raw method directly.

Current schema layer

gogram ships against a specific schema layer. The current ship is layer 227(mid-2026). Every constructor and method at that layer has a corresponding generated type; the full reference lives at tl-ref.

fmt.Println("schema layer", telegram.ApiVersion)
fmt.Println("schema layer", telegram.ApiVersion)

When Telegram bumps the schema, gogram regenerates against the new layer in the next minor release. Old constructors that get removed disappear; new ones show up; modified ones change shape.

Calling a raw method

// helper-less call to messages.sendReaction
_, err := client.MessagesSendReaction(&telegram.MessagesSendReactionParams{
	Peer:        &telegram.InputPeerUser{UserID: 123, AccessHash: hash},
	MsgID:       42,
	Reaction:    []telegram.Reaction{&telegram.ReactionEmoji{Emoticon: "๐Ÿ”ฅ"}},
	Big:         true,
	AddToRecent: true,
})
// helper-less call to messages.sendReaction
_, err := client.MessagesSendReaction(&telegram.MessagesSendReactionParams{
	Peer:        &telegram.InputPeerUser{UserID: 123, AccessHash: hash},
	MsgID:       42,
	Reaction:    []telegram.Reaction{&telegram.ReactionEmoji{Emoticon: "๐Ÿ”ฅ"}},
	Big:         true,
	AddToRecent: true,
})

Every method takes a *Paramsstruct and returns the TL result. Struct fields match the TL parameter names verbatim, exported and Go-cased. Flags (optional fields) are regular pointer-less fields with zero values meaning "not set."

Building input types

TL has explicit InputPeer, InputUser, InputChannel, InputDocument etc. variants for parameters; the matching output types (Peer, User, Channel) are different objects. When you have a high-level value like the result of ResolvePeer, you usually need to type-assert it to the specific Input variant:

peer, err := client.ResolvePeer("@durov")
ip, ok := peer.(*telegram.InputPeerUser)
if !ok {
	return errors.New("expected user peer")
}
peer, err := client.ResolvePeer("@durov")
ip, ok := peer.(*telegram.InputPeerUser)
if !ok {
	return errors.New("expected user peer")
}

For InputDocument/InputPhoto, the easiest path is to grab them from the message they live on: msg.Media().(*MessageMediaDocument).Document is already a usable input.

Finding the right method

The TL reference at tl-ref is searchable across every method, constructor, and type. Once you know the TL name (account.updateProfile), the Go name is mechanical (AccountUpdateProfile) and the params struct is AccountUpdateProfileParams.