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.
