Go Proton API
Official Go client library for the Proton REST API — covering Mail, Contacts, Calendar, Drive Shares, and account operations.

Overview & Quick Start

Overview & Quick Start
Go Proton API 
 Overview 
 github.com/ProtonMail/go-proton-api is the official Go library implementing a client for (a subset of) the Proton REST API. It covers Mail, Contacts, Calendar, Drive Shares , and core account operations. Built on resty/v2 for HTTP communication and gopenpgp/v2 for PGP encryption/decryption. 
 Quick Start 
 import (
 "context"
 "github.com/ProtonMail/go-proton-api"
)

// 1. Create a Manager (shared across clients)
m := proton.New()
defer m.Close()

ctx := context.Background()

// 2a. Login with username/password (full SRP flow)
c, auth, err := m.NewClientWithLogin(ctx, "user@proton.me", []byte("password"))
if err != nil {
 panic(err)
}
defer c.Close()

// Handle 2FA if needed
if auth.TwoFA.Enabled&proton.HasTOTP != 0 {
 if err := c.Auth2FA(ctx, proton.Auth2FAReq{TwoFactorCode: "123456"}); err != nil {
 panic(err)
 }
}

// 2b. Or create from existing tokens (no login needed)
c := m.NewClient("uid", "accessToken", "refreshToken")
defer c.Close()
 
 Installation 
 go get github.com/ProtonMail/go-proton-api
 
 Requires Go 1.26+ . 
 Architecture 
 The library uses a Manager → Client hierarchy: 
 
 Manager — Top-level factory object. Created via New(opts ...Option) . Holds a shared *resty.Client , status observers, error handlers, and panic recovery. Shared across all clients. 
 Client — Per-user client bound to a specific UID. Created by the Manager via NewClient() , NewClientWithLogin() , or NewClientWithRefresh() . Each Client handles one Proton account. 
 
 Core Features 
 
 
 
 Feature 
 Description 
 
 
 
 
 Automatic auth refresh 
 401 responses trigger transparent token refresh + retry 
 
 
 Connection status 
 Manager.AddStatusObserver() receives StatusUp / StatusDown notifications 
 
 
 Error handlers 
 Manager.AddErrorHandler(code, handler) — register callbacks for specific API error codes 
 
 
 Request hooks 
 Per-client and global pre/post request middleware via resty 
 
 
 Parallel operations 
 Delete messages, import messages, and attachment downloads all use parallel execution 
 
 
 Undo support 
 Label/unlabel operations return undo tokens for reversible actions 
 
 
 Paging helpers 
 GetAllContacts() , GetAllMessageIDs() etc. auto-page through results

Authentication & Sessions

Authentication & Sessions
Authentication & Sessions 
 The library handles the full authentication lifecycle: SRP-based login, 2FA/FIDO2, token refresh, session management, and user creation. 
 Login Methods 
 SRP Login (Username/Password) 
 Full Secure Remote Password login flow -- zero-knowledge password authentication: 
 m := proton.New()
ctx := context.Background()

c, auth, err := m.NewClientWithLogin(ctx, "user@proton.me", []byte("password"))
if err != nil {
 panic(err)
}
defer c.Close()
 
 2FA Support 
 If the account has 2FA enabled: 
 if auth.TwoFA.Enabled&proton.HasTOTP != 0 {
 if err := c.Auth2FA(ctx, proton.Auth2FAReq{TwoFactorCode: "123456"}); err != nil {
 panic(err)
 }
}
 
 FIDO2 Support 
 if auth.TwoFA.Enabled&proton.HasFIDO2 != 0 {
 if err := c.Auth2FA(ctx, proton.Auth2FAReq{
 FIDO2Data: proton.FIDO2Req{
 Attestation: "...",
 AuthenticatorData: "...",
 Signature: "...",
 CredentialID: "...",
 },
 }); err != nil {
 panic(err)
 }
}
 
 Refresh Token Login 
 Create a client from existing refresh token (no password needed): 
 c, _, err := m.NewClientWithRefresh(ctx, "uid", "refreshToken")
if err != nil {
 panic(err)
}
defer c.Close()
 
 Direct Token Login 
 Create a client when all auth info is already known: 
 c := m.NewClient("uid", "accessToken", "refreshToken")
defer c.Close()
 
 Session Management 
 // List active sessions
sessions, err := c.AuthSessions(ctx)

// Revoke a specific session
err := c.AuthRevoke(ctx, authUID)

// Revoke all sessions
err := c.AuthRevokeAll(ctx)

// Delete current session
err := c.AuthDelete(ctx)
 
 Auth Handlers 
 Register callbacks to handle auth events: 
 // Save new tokens when they are refreshed
c.AddAuthHandler(func(auth proton.Auth) {
 // Save auth.AccessToken, auth.RefreshToken to keychain
})

// Handle de-authentication
c.AddDeauthHandler(func() {
 // Clear stored credentials
})
 
 Key Types 
 
 
 
 Type 
 Description 
 
 
 
 
 Auth 
 Contains UID, AccessToken, RefreshToken, ServerProof, Scope, TwoFA info 
 
 
 AuthInfo 
 Server-provided auth metadata: salt, modulus, server ephemeral, 2FA status 
 
 
 TwoFAInfo / TwoFAStatus 
 Enum for TOTP, FIDO2, or both 
 
 
 FIDO2Req 
 FIDO2 authentication data (attestation, authenticator data, signature, credential ID) 
 
 
 AuthSession 
 Active session with client info and revocability flag 
 
 
 PasswordMode 
 One-password vs two-password mode 
 
 
 
 Security Notes 
 
 Login uses SRP (Secure Remote Password) protocol via go-srp for zero-knowledge password authentication 
 By default, the Manager verifies the server expected proof (verifyProofs flag) to detect MITM attacks 
 The server sends salt/modulus/ephemeral; the client computes proofs and verifies the server proof

Mail & Messages

Mail & Messages
Mail & Messages 
 Complete mail operations: fetching messages (metadata + full bodies), labeling, marking read/unread/forwarded, deleting, creating/updating/sending drafts, importing messages, building RFC822 MIME messages, and encrypting/decrypting with PGP. 
 Fetching Messages 
 Single Message 
 msg, err := c.GetMessage(ctx, messageID)
// msg contains: ID, addressID, labelIDs, subject, sender/recipients, flags, timestamps
 
 Full Message with Attachments 
 fullMsg, err := c.GetFullMessage(ctx, messageID, scheduler, storageProvider)
// fullMsg.Message + fullMsg.AttData ([][]byte of decoded attachment data)
 
 Message Metadata (List) 
 // Get all message metadata
meta, err := c.GetMessageMetadata(ctx, proton.MessageFilter{
 LabelIDs: []string{"inbox-label-id"},
})

// Paginated with filters
meta, err := c.GetMessageMetadataPage(ctx, page, pageSize, filter)

// Count messages
count, err := c.CountMessages(ctx)

// Get all message IDs (auto-paginating)
allIDs, err := c.GetAllMessageIDs(ctx, afterID)
 
 Drafts 
 // Create a draft
draft, err := c.CreateDraft(ctx, addrKR, proton.CreateDraftReq{
 Subject: "Hello",
 Sender: proton.Address{ID: "address-id"},
 To: []proton.Address{{Email: "recipient@example.com"}},
 Body: "Message body",
 MIMEType: "text/plain",
})

// Update a draft
updated, err := c.UpdateDraft(ctx, draftID, addrKR, proton.UpdateDraftReq{
 Body: "Updated body",
 Action: proton.DraftActionReply, // reply/reply-all/forward/auto-response/read-receipt
})

// Send a draft
err := c.SendDraft(ctx, draftID, proton.SendDraftReq{
 // MessagePackage with encrypted body + per-recipient key packets
})
 
 Bulk Operations 
 // Delete messages (parallel chunking, max 1000 per page)
err := c.DeleteMessage(ctx, id1, id2, id3...)

// Mark as read/unread
err := c.MarkMessagesRead(ctx, ids...)
err := c.MarkMessagesUnread(ctx, ids...)

// Mark as forwarded/unforwarded
err := c.MarkMessagesForwarded(ctx, ids...)
err := c.MarkMessagesUnForwarded(ctx, ids...)

// Label/unlabel with undo support
err := c.LabelMessages(ctx, ids, labelID)
err := c.UnlabelMessages(ctx, ids, labelID)
// Returns undo tokens for reversible actions
 
 Message Import 
 Bulk import of raw RFC822 messages: 
 stream := c.ImportMessages(ctx, addrKR, workers, buffer, req1, req2, ...)
// req := proton.ImportReq{
// RawMessage: []byte("Raw RFC822 message..."),
// AddressID: "address-id",
// LabelIDs: []string{"label-id"},
// }

for res := range stream {
 fmt.Println(res.ID, res.Error)
}
 
 MIME Building & Encryption 
 // Reconstruct full RFC822 MIME from decrypted parts
rawMIME, err := proton.BuildRFC822(kr, msg, attData)

// Encrypt a raw RFC822 message
encrypted, err := proton.EncryptRFC822(kr, literal)
 
 Key Types 
 
 
 
 Type 
 Description 
 
 
 
 
 MessageMetadata 
 ID, addressID, labelIDs, subject, sender/recipients, flags, timestamps 
 
 
 Message 
 Extends Metadata with raw Header, Headers, encrypted Body, MIMEType, Attachments 
 
 
 FullMessage 
 Message + decoded attachment data 
 
 
 MessageFilter 
 Filter by ID list, subject, addressID, externalID, labelID 
 
 
 MessageFlag 
 Bitmask: received, sent, internal, E2E, replied, forwarded, spam/DMARC/DKIM/SPF 
 
 
 EncryptionScheme 
 InternalScheme, EncryptedOutsideScheme, ClearScheme, PGPInlineScheme, PGPMIMEScheme 
 
 
 SendPreferences 
 Per-recipient encryption/signature/MIME type preferences 
 
 
 MessagePackage 
 Encrypted message body + per-recipient key packets 
 
 
 DraftTemplate 
 Subject, sender, recipients, body, MIME type for draft creation 
 
 
 ImportReq / ImportMetadata 
 Import request with raw message bytes and metadata

Contacts, Calendar & Labels

Contacts
Contacts 
 CRUD operations for contacts and contact emails via /contacts/v4 . 
 Operations 
 Get Contacts 
 // Single contact by ID
contact, err := c.GetContact(ctx, contactID)

// Count contacts
count, err := c.CountContacts(ctx)

// Paginated listing
contacts, err := c.GetContacts(ctx, page, pageSize)

// Auto-paginating - get all
allContacts, err := c.GetAllContacts(ctx)

// Auto-paginating with custom page size
allContacts, err := c.GetAllContactsPaged(ctx, pageSize)
 
 Search by Email 
 // Count matches
count, err := c.CountContactEmails(ctx, "user@example.com")

// Paginated search
emails, err := c.GetContactEmails(ctx, "user@example.com", page, pageSize)

// Get all matching
allEmails, err := c.GetAllContactEmails(ctx, "user@example.com")
 
 Create / Update / Delete 
 // Bulk create with overwrite and label options
results, err := c.CreateContacts(ctx, proton.CreateContactsReq{
 Contacts: []proton.Contact{
 {
 Cards: []proton.ContactCard{...},
 Email: "user@example.com",
 },
 },
})

// Update a contact
updated, err := c.UpdateContact(ctx, contactID, proton.UpdateContactReq{
 Cards: []proton.ContactCard{...},
})

// Bulk delete
err := c.DeleteContacts(ctx, proton.DeleteContactsReq{
 IDs: []string{id1, id2},
})
 
 Per-Contact Encryption Preferences 
 Contact settings are stored as VCard-embedded X-PM-* fields: 
 // Get encryption settings for a contact
settings := contact.GetSettings()
// Returns: MIME type, PGP scheme (inline/MIME), sign/encrypt flags, PGP keys

// Set encryption settings
contact.SetSettings(proton.ContactSettings{
 MIMEType: "text/plain",
 Scheme: proton.PGPMIMEScheme,
 Sign: true,
 Encrypt: true,
 EncryptUntrusted: true,
 PGPKeys: []proton.PGPKey{...},
})
 
 Key Types 
 
 
 
 Type 
 Description 
 
 
 
 
 Contact 
 Composite of ContactMetadata and ContactCards 
 
 
 ContactMetadata 
 ID, name, UID, size, timestamps, emails, labels 
 
 
 ContactCards 
 VCard data for the contact 
 
 
 ContactEmail 
 Email entry with ID, name, email string, type tags, contactID, labels 
 
 
 ContactSettings 
 Per-contact encryption preferences (MIME type, scheme, sign/encrypt flags, PGP keys) 
 
 
 RecipientType 
 Internal vs external recipient classification

Calendar
Calendar 
 Calendar and calendar event retrieval via /calendar/v1 . Includes key management for encrypted calendar data. 
 Operations 
 Calendars 
 // List all calendars
calendars, err := c.GetCalendars(ctx)

// Get a single calendar
cal, err := c.GetCalendar(ctx, calendarID)

// Get encryption keys for a calendar
keys, err := c.GetCalendarKeys(ctx, calendarID)

// Get shared calendar members
members, err := c.GetCalendarMembers(ctx, calendarID)

// Get passphrase for calendar key decryption
passphrase, err := c.GetCalendarPassphrase(ctx, calendarID)
 
 Calendar Events 
 // Count events
count, err := c.CountCalendarEvents(ctx, calendarID)

// Paginated listing with optional filters
events, err := c.GetCalendarEvents(ctx, calendarID, page, pageSize, filter)

// Auto-paginating - get all events
allEvents, err := c.GetAllCalendarEvents(ctx, calendarID, filter)

// Single event by ID
event, err := c.GetCalendarEvent(ctx, calendarID, eventID)
 
 Decryption 
 Calendar events use two-layer encryption : 
 
 Calendar keys (passphrase-locked) 
 Shared events add a second layer of sharing key packets 
 
 // Unlock calendar keys with passphrase
keyRing, err := keys.Unlock(passphrase)

// Decrypt individual event parts
decoded, err := eventPart.Decode(calKR, addrKR, keyPacket)
// Handles both two-layer decryption and PGP signature verification
 
 Key Types 
 
 
 
 Type 
 Description 
 
 
 
 
 Calendar 
 ID, name, description, color, display flag, type (normal/subscribed), flags 
 
 
 CalendarKey 
 Encrypted private key with Unlock(passphrase) returning *crypto.Key 
 
 
 CalendarKeys 
 Slice of keys with Unlock(passphrase) returning *crypto.KeyRing 
 
 
 CalendarMember 
 ID, permissions, email, color, display, calendarID 
 
 
 CalendarPassphrase 
 Encrypted passphrase with Decrypt(memberID, addrKR) for decryption 
 
 
 CalendarEvent 
 UID, start/end times, timezone, full-day flag, author, attendees, encrypted parts 
 
 
 CalendarEventPart 
 Individual part with type (clear/encrypted/signed), data, signature, author 
 
 
 EventAction 
 Delete, Create, Update, UpdateFlags 
 
 
 
 PGP Signature Verification 
 Encrypted calendar parts can also be signed. The Decode method verifies signatures using the address keyring.

Labels & Addresses
Labels 
 Label (category/folder) management via /core/v4/labels . 
 Operations 
 // Fetch labels filtered by type(s)
labels, err := c.GetLabels(ctx, proton.LabelLabel, proton.LabelFolder)

// Find a specific label by ID
label, err := c.GetLabel(ctx, labelID, proton.LabelLabel)

// Create a label
label, err := c.CreateLabel(ctx, proton.CreateLabelReq{
 Name: "Personal",
 Color: "#ff0000",
 Type: proton.LabelLabel,
 Parent: parentID,
})

// Update a label
err := c.UpdateLabel(ctx, labelID, proton.UpdateLabelReq{
 Name: "Updated Name",
 Color: "#00ff00",
})

// Delete a label
err := c.DeleteLabel(ctx, labelID)
 
 System Label Constants 
 
 
 
 Constant 
 ID 
 Description 
 
 
 
 
 InboxLabel 
 "0" 
 Inbox 
 
 
 AllSentLabel 
 "2" 
 All Sent 
 
 
 TrashLabel 
 "3" 
 Trash 
 
 
 SpamLabel 
 "4" 
 Spam 
 
 
 AllMailLabel 
 "5" 
 All Mail 
 
 
 StarredLabel 
 "10" 
 Starred 
 
 
 
 Email Addresses 
 Email address management via /core/v4/addresses . 
 Operations 
 // List addresses (sorted by order)
addresses, err := c.GetAddresses(ctx)

// Get a single address
addr, err := c.GetAddress(ctx, addressID)

// Reorder addresses
err := c.OrderAddresses(ctx, proton.OrderAddressesReq{...})

// Enable/disable address
err := c.EnableAddress(ctx, addressID)
err := c.DisableAddress(ctx, addressID)

// Delete address
err := c.DeleteAddress(ctx, addressID)
 
 Address Types 
 
 
 
 Type 
 Description 
 
 
 
 
 Original 
 Original Proton address 
 
 
 Alias 
 Proton alias 
 
 
 Custom 
 Custom domain address 
 
 
 Premium 
 Premium address 
 
 
 External 
 Bring Your Own Email (BYOE) 
 
 
 
 BYOE Detection 
 if addr.IsBYOEAddress() {
 // External type with sending enabled
}
 
 Key Types 
 
 
 
 Type 
 Description 
 
 
 
 
 Label 
 ID, parentID, name, path ([]string), color, type 
 
 
 LabelType 
 Label, ContactGroup, Folder, System 
 
 
 Address 
 ID, email, send/receive flags, status, type, order, display name, keys 
 
 
 AddressStatus 
 Disabled, enabled, deleting 
 
 
 AddressType 
 Original, alias, custom, premium, external 
 
 
 
 Path Handling 
 The API sends label path as a string (e.g., "Inbox/SubFolder" ), which is split into a []string slice for Go use, with custom MarshalJSON / UnmarshalJSON .

Attachments, Shares & Events

Attachments & Drive Shares
Attachments 
 Attachment upload and download via /mail/v4/attachments . 
 Upload 
 attachment, err := c.UploadAttachment(ctx, addrKR, proton.CreateAttachmentReq{
 MessageID: "message-id",
 Filename: "document.pdf",
 MIMEType: "application/pdf",
 Disposition: proton.AttachmentDisposition,
 Body: fileBytes,
})
 
 Upload is encrypted with the address keyring and signed (detached) before upload. The server stores the encrypted KeyPacket , DataPacket , and Signature separately. 
 Download 
 // Download as bytes
data, err := c.GetAttachment(ctx, attachmentID)

// Stream into io.ReaderFrom
err := c.GetAttachmentInto(ctx, attachmentID, reader)
 
 Key Types 
 
 
 
 Type 
 Description 
 
 
 
 
 Attachment 
 ID, name, size, MIME type, disposition (inline/attachment), headers, key packets, signature 
 
 
 CreateAttachmentReq 
 MessageID, filename, MIME type, disposition, content ID, body bytes 
 
 
 Disposition 
 InlineDisposition, AttachmentDisposition 
 
 
 
 
 Drive Shares 
 Proton Drive share management via /drive/shares . 
 Operations 
 // List shares
shares, err := c.ListShares(ctx, all)

// Get a single share with full key material
share, err := c.GetShare(ctx, shareID)
 
 Key Derivation 
 Shares use a two-step PGP decryption + passphrase unlock process: 
 // Decrypt passphrase with address keyring, verify signature, unlock share key
keyRing, err := share.GetKeyRing(addrKR)
// Returns *crypto.KeyRing for Drive access
 
 Key Types 
 
 
 
 Type 
 Description 
 
 
 
 
 ShareMetadata 
 ShareID, linkID, volumeID, type, state, creation/modify times, creator email 
 
 
 Share 
 Extends metadata with addressID, addressKeyID, encrypted key, passphrase, passphrase signature 
 
 
 ShareType 
 Main (1), Standard (2), Device (3) 
 
 
 ShareState 
 Active (1), Deleted (2)

Real-time Events & User Account
Real-time Events 
 Event polling and streaming for real-time notifications via /core/v4/events . 
 Event Streaming 
 // Get the latest event ID
fromEventID, err := c.GetLatestEventID(context.Background())

// Create a new event streamer
for event := range c.NewEventStream(ctx, 20*time.Second, 20*time.Second, fromEventID) {
 fmt.Println(event.EventID)
 // Process event.User, event.Messages, event.Labels, etc.
}
 
 The event stream uses a custom NewTicker with random jitter to avoid thundering herd. 
 Event Polling 
 // Fetch events starting after eventID
events, more, err := c.GetEvent(ctx, eventID)
// Returns up to 50 events per call with more indicating continuation
 
 Event Types 
 The Event struct contains typed sub-events: 
 event.User // User changes
event.UserSettings // User settings changes
event.MailSettings // Mail settings changes
event.Messages // Message changes ([]MessageEvent)
event.Labels // Label changes ([]LabelEvent)
event.Addresses // Address changes ([]AddressEvent)
event.Notifications // Notifications ([]NotificationEvent)
event.UsedSpace // Storage usage changes
 
 Key Types 
 
 
 
 Type 
 Description 
 
 
 
 
 Event 
 Contains eventID, refresh flags, and typed sub-events 
 
 
 RefreshFlag 
 RefreshMail (1), RefreshAll (255) 
 
 
 EventAction 
 Delete, Create, Update, UpdateFlags 
 
 
 MessageEvent 
 EventItem (ID + Action) with full Message entity 
 
 
 LabelEvent 
 EventItem (ID + Action) with full Label entity 
 
 
 AddressEvent 
 EventItem (ID + Action) with full Address entity 
 
 
 
 
 User Account 
 User account operations via /core/v4/users . 
 Operations 
 // Fetch current user info
user, err := c.GetUser(ctx)

// With human verification token
user, err := c.GetUserWithHV(ctx, hvToken)

// Delete account (requires SRP proof re-authentication)
err := c.DeleteUser(ctx, password, proton.DeleteUserReq{
 Reason: "no longer needed",
 Feedback: "test account",
})
 
 Key Types 
 
 
 
 Type 
 Description 
 
 
 
 
 User 
 ID, name, display name, email, keys, used/max/upload space limits, credit, currency 
 
 
 ProductUsedSpace 
 Space breakdown by calendar, contact, drive, mail, pass (password manager) 
 
 
 DeleteUserReq 
 Reason, feedback, email confirmation 
 
 
 
 Feature Flags 
 Fetch feature flag/toggle results from the Proton API: 
 features, err := c.GetFeatures(ctx, "sticky-key-uuid")
// Returns FeatureFlagResult for a given UUID sticky key