qwicmail
qwicmail docs

Send your first email

Roughly ten minutes from "approved tenant" to a delivered receipt. This guide assumes your access request has been approved and you can sign in at app.qwicmail.com.

Not approved yet? qwicmail uses closed onboarding — every tenant is manually verified before sending is enabled. If you haven't submitted a request, start at /request. Approval usually lands within one business day.

1. Install the SDK

Pick the SDK for your language. Every example on this page also has a raw curl tab, so you can integrate from any HTTP client if we don't ship an official SDK for your stack yet.

# No install needed.
export QWICMAIL_API_KEY="qm_live_xxxxxxxxxxxx"
npm install @qwicmail/sdk
# or: pnpm add @qwicmail/sdk  |  yarn add @qwicmail/sdk
pip install qwicmail
go get gitlab.com/teqpace/qwicmail-go
gem install qwicmail
composer require qwicmail/qwicmail-php
<!-- Maven -->
<dependency>
  <groupId>com.qwicmail</groupId>
  <artifactId>qwicmail-java</artifactId>
  <version>0.1.0</version>
</dependency>

// Gradle (Kotlin DSL)
implementation("com.qwicmail:qwicmail-java:0.1.0")

2. Mint an API key

In the customer portal, open Settings → API keys → New key. Give the key a name (we recommend the environment it's for — production, staging) and grant the emails:send scope. Two other scopes exist (domains:write, webhooks:write) which you only need if you intend to drive those resources from your code rather than from the portal.

The plaintext key (format qm_live_…) is shown exactly once. Copy it into your secret manager before closing the dialog — there is no way to recover it afterwards. If lost, revoke it and mint a fresh one.

3. Add and verify a sending domain

You can only send mail from a domain that's been verified for your tenant. The simplest path is the portal:

  1. Open Sending → Domains → Add domain.
  2. Enter the domain (e.g. mail.example.com).
  3. Publish the three DNS records the portal prints — one DKIM (required), one SPF (recommended), one DMARC (recommended in monitoring mode).
  4. Click Verify. Verification queries your authoritative nameservers directly and either marks the domain verified or returns the specific failure reason.

The same flow is available over the API — see the domains reference. Whichever path you use, DKIM is the only record verification depends on; SPF and DMARC are advisory but strongly recommended (and required by Gmail / Yahoo bulk sender rules above 5,000 messages/day).

4. Send

Once the domain is verified, every API key with emails:send can submit mail from any address on it:

curl https://api.qwicmail.com/emails \
  -H "Authorization: Bearer $QWICMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from":    "alerts@mail.example.com",
    "to":      "customer@example.org",
    "subject": "Your receipt",
    "html":    "<p>Thanks for your order.</p>"
  }'
import { Qwicmail } from "@qwicmail/sdk";

const qm = new Qwicmail({ apiKey: process.env.QWICMAIL_API_KEY! });

const res = await qm.emails.send({
  from:    "alerts@mail.example.com",
  to:      "customer@example.org",
  subject: "Your receipt",
  html:    "<p>Thanks for your order.</p>",
});

console.log(res.message_ids);
import os
from qwicmail import Qwicmail

qm = Qwicmail(api_key=os.environ["QWICMAIL_API_KEY"])

res = qm.emails.send(
    from_="alerts@mail.example.com",
    to="customer@example.org",
    subject="Your receipt",
    html="<p>Thanks for your order.</p>",
)

print(res.message_ids)
package main

import (
    "context"
    "fmt"
    "os"

    "gitlab.com/teqpace/qwicmail-go"
)

func main() {
    qm := qwicmail.New(os.Getenv("QWICMAIL_API_KEY"))

    res, err := qm.Emails.Send(context.Background(), &qwicmail.SendRequest{
        From:    "alerts@mail.example.com",
        To:      []string{"customer@example.org"},
        Subject: "Your receipt",
        HTML:    "<p>Thanks for your order.</p>",
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(res.MessageIDs)
}
require "qwicmail"

qm = Qwicmail::Client.new(api_key: ENV.fetch("QWICMAIL_API_KEY"))

res = qm.emails.send(
  from:    "alerts@mail.example.com",
  to:      "customer@example.org",
  subject: "Your receipt",
  html:    "<p>Thanks for your order.</p>",
)

puts res.message_ids
<?php
require __DIR__ . "/vendor/autoload.php";

use Qwicmail\Client;

$qm = new Client(getenv("QWICMAIL_API_KEY"));

$res = $qm->emails->send([
    "from"    => "alerts@mail.example.com",
    "to"      => "customer@example.org",
    "subject" => "Your receipt",
    "html"    => "<p>Thanks for your order.</p>",
]);

print_r($res->messageIds);
import com.qwicmail.Qwicmail;
import com.qwicmail.SendRequest;
import com.qwicmail.SendResponse;

var qm = new Qwicmail(System.getenv("QWICMAIL_API_KEY"));

SendResponse res = qm.emails().send(SendRequest.builder()
    .from("alerts@mail.example.com")
    .to("customer@example.org")
    .subject("Your receipt")
    .html("<p>Thanks for your order.</p>")
    .build());

System.out.println(res.messageIds());

A successful submission returns 202 Accepted with the IDs the platform assigned:

JSON response
{
  "id":          "1f0c2c1a-...",
  "message_ids": ["8a3f1f5b-..."],
  "rejected":    [],
  "replayed":    false
}

Each message_id traces the email end-to-end. The same value is set as the SMTP Message-ID header on the outgoing wire, and appears in every webhook event the message produces. If you ever need to ask support about a single message, that's the value to quote.

What if a recipient is on the suppression list?

Recipients on your tenant suppression list (or the platform-global hard-bounce list) never reach the queue. They show up in the rejected array with a reason such as hard_bounce or complaint — the rest of the recipients in the call are accepted normally. If every recipient is suppressed, the response is 422 Unprocessable Entity with error: "all_recipients_suppressed".

5. (Optional) Listen for delivery events

Once your sending is in production, register an endpoint to receive delivery, bounce, complaint, and open events as HTTP POSTs. The payloads, retry schedule, and signature-verification recipes are documented under Webhooks.

6. Go live

Two things to know before you switch production traffic to qwicmail:

  • Tier escalation is automatic. New tenants start with a conservative 24-hour sending cap. The cap relaxes on day 14 and is removed entirely on day 30, provided your bounce and complaint rates stay clean. No paperwork — just send sensibly.
  • Auto-throttle protects everyone. If 24-hour bounce or complaint rates cross our thresholds, the platform throttles your tenant and surfaces the reason in the portal. Throttle lifts automatically after 7 days of clean sending.

For the practical deliverability checklist (DNS, warm-up posture, list hygiene) see Deliverability.