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