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

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:

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

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

Mail & Messages

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, 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
Contacts, Calendar & Labels

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:

  1. Calendar keys (passphrase-locked)
  2. 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.

Contacts, Calendar & Labels

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, 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)
Attachments, Shares & Events

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