Official SDKs
Idiomatic clients for six runtimes. Every SDK ships the same surface:
a typed send, a webhook-signature verifier, and automatic
retry on 429 and 5xx responses. Each is
semver-versioned and published to its language's standard registry.
At a glance
| SDK | Install | Package | Min runtime |
|---|---|---|---|
| TypeScript / Node | npm install @qwicmail/sdk | @qwicmail/sdk (npm) | Node 18+, modern browsers |
| Python | pip install qwicmail | qwicmail (PyPI) | Python 3.9+ |
| Go | go get gitlab.com/teqpace/qwicmail-go | qwicmail-go | Go 1.21+ |
| Ruby | gem install qwicmail | qwicmail (RubyGems) | Ruby 3.0+ |
| PHP | composer require qwicmail/qwicmail-php | qwicmail/qwicmail-php | PHP 8.1+ |
| Java / Kotlin | see below | com.qwicmail:qwicmail-java | JDK 11+ |
Initialise the client
import { Qwicmail } from "@qwicmail/sdk";
const qm = new Qwicmail({
apiKey: process.env.QWICMAIL_API_KEY!,
baseUrl: "https://api.qwicmail.com", // default
timeout: 30_000, // ms, default
maxRetries: 3, // default; set 0 to disable
});import os
from qwicmail import Qwicmail
qm = Qwicmail(
api_key=os.environ["QWICMAIL_API_KEY"],
base_url="https://api.qwicmail.com", # default
timeout=30.0, # seconds, default
max_retries=3, # default
)import "gitlab.com/teqpace/qwicmail-go"
qm := qwicmail.New(os.Getenv("QWICMAIL_API_KEY"),
qwicmail.WithBaseURL("https://api.qwicmail.com"), // default
qwicmail.WithTimeout(30 * time.Second),
qwicmail.WithMaxRetries(3),
)require "qwicmail"
qm = Qwicmail::Client.new(
api_key: ENV.fetch("QWICMAIL_API_KEY"),
base_url: "https://api.qwicmail.com", # default
timeout: 30, # seconds, default
max_retries: 3, # default
)use Qwicmail\Client;
$qm = new Client(getenv("QWICMAIL_API_KEY"), [
"base_url" => "https://api.qwicmail.com",
"timeout" => 30,
"max_retries" => 3,
]);import com.qwicmail.Qwicmail;
import java.time.Duration;
var qm = Qwicmail.builder()
.apiKey(System.getenv("QWICMAIL_API_KEY"))
.baseUrl("https://api.qwicmail.com") // default
.timeout(Duration.ofSeconds(30))
.maxRetries(3)
.build();Send a message
The send call accepts either an html/text body
directly, or a template_id + data pair if you
want server-side rendering. See Templates
for the templating side; the SDKs accept both shapes via the same
method.
const res = await qm.emails.send({
from: "alerts@mail.example.com",
to: ["customer@example.org"],
subject: "Order shipped",
html: "<p>Your order is on the way.</p>",
});
console.log(res.message_ids); // [ "8a3f1f5b-…" ]res = qm.emails.send(
from_="alerts@mail.example.com",
to=["customer@example.org"],
subject="Order shipped",
html="<p>Your order is on the way.</p>",
)
print(res.message_ids)res, err := qm.Emails.Send(ctx, &qwicmail.SendRequest{
From: "alerts@mail.example.com",
To: []string{"customer@example.org"},
Subject: "Order shipped",
HTML: "<p>Your order is on the way.</p>",
})
if err != nil {
return err
}
log.Println(res.MessageIDs)res = qm.emails.send(
from: "alerts@mail.example.com",
to: ["customer@example.org"],
subject: "Order shipped",
html: "<p>Your order is on the way.</p>",
)
puts res.message_ids$res = $qm->emails->send([
"from" => "alerts@mail.example.com",
"to" => ["customer@example.org"],
"subject" => "Order shipped",
"html" => "<p>Your order is on the way.</p>",
]);
print_r($res->messageIds);SendResponse res = qm.emails().send(SendRequest.builder()
.from("alerts@mail.example.com")
.to("customer@example.org")
.subject("Order shipped")
.html("<p>Your order is on the way.</p>")
.build());
System.out.println(res.messageIds());Automatic retry policy
Every SDK retries idempotent requests on transport errors,
429, and 5xx with exponential backoff plus
jitter. Retry-After is honoured exactly when present.
Non-idempotent POST /emails calls are only retried when an
Idempotency-Key is set — the SDKs generate one
automatically unless you supply your own.
| Condition | Action |
|---|---|
| Network / timeout | Retry up to max_retries times. |
429 Too Many Requests | Sleep Retry-After, then retry. |
5xx | Exponential backoff (250ms, 1s, 4s) + jitter, then retry. |
4xx (other) | Surface as a typed error; no retry. |
Errors
Every SDK exposes a typed error hierarchy. The shape is the same across languages:
AuthError—401, key missing or revoked.ScopeError—403 scope_required.TenantNotActiveError—403 tenant_not_active(suspended / throttled / pending).ValidationError—4xxvalidation failures.RateLimitError—429after retries exhausted.ServerError—5xxafter retries exhausted.NetworkError— DNS / TCP / TLS / timeout, no HTTP response received.
Webhook signature verification
Every SDK exposes a one-line verifier — see the full recipe and language samples on the Webhooks page.
Installing the Java/Kotlin SDK
The same artifact works from both languages.
<dependency>
<groupId>com.qwicmail</groupId>
<artifactId>qwicmail-java</artifactId>
<version>0.1.0</version>
</dependency>implementation("com.qwicmail:qwicmail-java:0.1.0")implementation 'com.qwicmail:qwicmail-java:0.1.0'Source & issues
Every SDK lives in the qwicmail monorepo under sdks/<lang>. Issues and PRs are welcome on GitLab.