🚀 Getting Started

📜

1. How to purchase a license

Go to Account → 📜 Licenses.
Choose the number of licenses and billing period (monthly or yearly).
Click Buy Licenses and complete the payment.
Licenses are activated immediately after successful payment.

🌐

2. How to add your domain

Go to My Account → 🌐 Domains.
Click + Add Domain and enter your domain name.
Press Verify. Verification happens automatically within a few minutes.
The system will display DNS records — add them to your domain's DNS manager.

2a. Or buy a domain in the 🌐 + Buy domain section — find your domain with the search tool and complete the purchase.

📧

3. How to create an email account

Go to My Account → 📧 Email Accounts.
Click + Add Email Account.
Choose a domain, enter a username and a strong password (min. 12 characters).
The email account is ready to use immediately after creation.

📬 Email Client Setup

⚡

Mozilla Thunderbird

Open Thunderbird → Account → Add email account.
Enter your name, email and password. Thunderbird usually detects settings automatically.

POP3 server: mail.promail.lv, ports 995, SSL/TLS Normal pass.
SMTP server: mail.promail.lv, ports 465, SSL/TLS Normal pass.
Username: full email address
Password: your account password
🌍

Roundcube Webmail

Use email directly in your browser — no installation required.

Username: full email address
Password: your account password
Open Webmail →
📜
1. Buy a license
→
🌐
2. Add a domain
→
📧
3. Create email
→
✅
Done!
FOR DEVELOPERS
👨‍💻

Advanced guide

The section below is for web developers who want to add a "Sign in with ProMail" button to their website.

✅ If you use ProMail for email — everything you need is described above. You can skip this section.

🛠️ If you are building a website — below you will find the configuration, code examples and common mistakes.

🔐 Sign in with ProMail on your website

Let people sign in to your website with their ProMail account instead of creating another password. ProMail handles the passwords, passkeys and fingerprint prompts; your site receives a verified identity.

ProMail is a standard OpenID Connect provider. If your framework has an OIDC library, integration takes about 10 minutes — you will not need to write any cryptography.

⚙️ How it works

1️⃣

Your site sends the user to ProMail

They click your button and are redirected to promail.lv.

2️⃣

ProMail signs them in

Passkey, fingerprint or password. Your site never sees any of it.

3️⃣

The user returns with a code

Your server exchanges that code for an identity token, behind the scenes.

The user's password never reaches your site. If your site is ever breached, there are no ProMail passwords in it to steal.

🔑 How to get your credentials

Go to Account → 🔑 Applications and click ➕ Add application.
Enter an application name and a redirect URI.

Each application uses one license slot. The same license can serve a domain and an email account at the same time — they do not take slots from each other.

⚠️ The Client Secret is shown only once. Copy it immediately. If you lose it you can generate a new one, but the old one stops working.

📋 Configuration

Discovery URL: https://promail.lv/.well-known/openid-configuration
Issuer: https://promail.lv
Authorization: https://promail.lv/oidc/authorize
Token: https://promail.lv/oidc/token
User info: https://promail.lv/oidc/userinfo
Public keys: https://promail.lv/.well-known/jwks.json
Scopes: openid email profile
Flow: authorization_code + PKCE (S256) — required
Signing: RS256

💡 Most libraries need only the Discovery URL — they read everything else from it automatically, including the signing keys.

⚡ Quick start with a library

Recommended. Point your OIDC library at the Discovery URL.

WordPress

Install the plugin OpenID Connect Generic Client, then set:

Client ID       : your client id
Client Secret   : your client secret
OpenID Scope    : openid email profile
Login Endpoint  : https://promail.lv/oidc/authorize
Userinfo        : https://promail.lv/oidc/userinfo
Token Validation: https://promail.lv/oidc/token
Identity Key    : sub

Node.js

Package openid-client:

const promail = await Issuer.discover(
  'https://promail.lv'
);

const client = new promail.Client({
  client_id:      CLIENT_ID,
  client_secret:  CLIENT_SECRET,
  redirect_uris:  [REDIRECT_URI],
  response_types: ['code'],
});

Laravel / PHP

Package jumbojett/openid-connect-php:

$oidc = new OpenIDConnectClient(
    'https://promail.lv',
    CLIENT_ID,
    CLIENT_SECRET
);
$oidc->addScope(['openid','email','profile']);
$oidc->setCodeChallengeMethod('S256');
$oidc->authenticate();

$sub = $oidc->requestUserInfo('sub');

Python

Library Authlib:

oauth.register(
  name='promail',
  server_metadata_url=
    'https://promail.lv/.well-known/'
    'openid-configuration',
  client_id=CLIENT_ID,
  client_secret=CLIENT_SECRET,
  client_kwargs={
    'scope': 'openid email profile',
    'code_challenge_method': 'S256',
  },
)

🛠️ Plain PHP, no library

A complete working example. Two files: one starts the flow, one handles the return.

1. promail-login.php — the button links here

<?php
session_start();

define('ISSUER',        'https://promail.lv');
define('CLIENT_ID',     'your_client_id');
define('CLIENT_SECRET', 'your_client_secret');
define('REDIRECT_URI',  'https://yoursite.lv/promail-callback');

function b64url($b) {
    return rtrim(strtr(base64_encode($b), '+/', '-_'), '=');
}

// PKCE. The verifier stays on your server; only its hash goes in the URL,
// so a stolen code cannot be redeemed without it.
$verifier = b64url(random_bytes(32));
$_SESSION['pm_verifier'] = $verifier;
$_SESSION['pm_state']    = $state = b64url(random_bytes(16));
$_SESSION['pm_nonce']    = $nonce = b64url(random_bytes(16));

header('Location: ' . ISSUER . '/oidc/authorize?' . http_build_query([
    'client_id'             => CLIENT_ID,
    'redirect_uri'          => REDIRECT_URI,
    'response_type'         => 'code',
    'scope'                 => 'openid email profile',
    'state'                 => $state,
    'nonce'                 => $nonce,
    'code_challenge'        => b64url(hash('sha256', $verifier, true)),
    'code_challenge_method' => 'S256',
]));

2. promail-callback.php — the redirect URI points here

<?php
session_start();
// ... the same define() lines as above ...

if (isset($_GET['error'])) {
    exit('Sign-in failed: ' . htmlspecialchars($_GET['error']));
}

// state must match, or this is a forged request from another site
if (!hash_equals($_SESSION['pm_state'] ?? '', $_GET['state'] ?? '')) {
    exit('Security check failed');
}

// Exchange the code. Server to server over TLS - the browser is not
// involved, which is why it is safe to send the client secret here.
$ch = curl_init(ISSUER . '/oidc/token');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POSTFIELDS     => http_build_query([
        'grant_type'    => 'authorization_code',
        'code'          => $_GET['code'],
        'redirect_uri'  => REDIRECT_URI,
        'code_verifier' => $_SESSION['pm_verifier'],
        'client_id'     => CLIENT_ID,
        'client_secret' => CLIENT_SECRET,
    ]),
]);
$tokens = json_decode(curl_exec($ch), true);
curl_close($ch);

if (empty($tokens['id_token'])) { exit('Token exchange failed'); }

$claims = json_decode(base64_decode(strtr(
    explode('.', $tokens['id_token'])[1], '-_', '+/')), true);

if ($claims['iss'] !== ISSUER)    { exit('Wrong issuer'); }
if ($claims['aud'] !== CLIENT_ID) { exit('Wrong audience'); }
if ($claims['exp'] < time())      { exit('Token expired'); }
if ($claims['nonce'] !== $_SESSION['pm_nonce']) { exit('Nonce mismatch'); }

unset($_SESSION['pm_verifier'], $_SESSION['pm_state'], $_SESSION['pm_nonce']);

// $claims['sub']   - permanent user id. STORE THIS.
// $claims['email'] - may change. Do not use as the key.
// $claims['name']

$user = findUserByPromailSub($claims['sub']);
if (!$user) {
    // First time. Link to an existing account, or create a new one.
}

⚠️ Common mistakes

Using the email as the account key

Use sub. It never changes. Emails do, and a reused address would hand the account to whoever gets it next.

Redirect URI does not match exactly

ProMail compares it byte for byte. https://site.lv/cb and https://site.lv/cb/ are two different addresses. This is the most common mistake.

Trusting the token's own alg field

Always require RS256 from your own configuration. A token claiming alg: none must be rejected.

Skipping the state check

Without it, an attacker can complete a login flow in the victim's browser and connect their own ProMail account to the victim's session.

Putting the Client Secret in JavaScript

It belongs on your server only. Anything in browser code is public.

Leaving out PKCE

ProMail requires it. Requests without code_challenge are refused.

🎨 The button

Use consistent wording and appearance so people recognise the button across sites.

🔐 Sign in with ProMail
<a href="/promail-login" class="promail-btn">
  <img src="https://promail.lv/img/promail-logo.webp" alt="" width="20" height="20">
  <span>Sign in with ProMail</span>
</a>

<style>
.promail-btn{display:inline-flex;align-items:center;justify-content:center;
  gap:9px;padding:11px 18px;border:1px solid #d1d5db;border-radius:8px;
  background:#fff;color:#111827;font:600 14px system-ui,sans-serif;
  text-decoration:none}
.promail-btn:hover{background:#f9fafb;border-color:#9ca3af}
</style>
English: Sign in with ProMail
Latviski: Pieteikties ar ProMail
По-русски: Войти через ProMail

🧪 Testing

There is no separate sandbox. Register a second application with a http://localhost redirect URI — localhost is the only address allowed without https — and test your integration before going live.

🔑
1. Register the app
→
⚙️
2. Add the config
→
🎨
3. Add the button
→
✅
Done!