SDKs

Official, MIT-licensed client libraries for Node.js, Python, PHP, Go and Ruby — plus a command-line tool and an MCP server. Consistent interface across every language.

Node.js Python PHP Go Ruby CLI MCP

Every SDK wraps the REST API with idiomatic types, an QUOLLE_API_KEY environment-variable fallback, safe retry-with-backoff, and webhook signature verification. Prefer raw HTTP? See the API Reference.

Install

LanguageInstallSource
Pythonpip install quollequolle-python
PHPcomposer require quolle/quolle-phpquolle-php
Gogo get github.com/Quolle-main/quolle-goquolle-go
Rubygem install quollequolle-ruby
Node.js / TypeScriptComing soon @quolle/sdkquolle-nodejs
CLIComing soon quollequolle-cli
MCP serverComing soon @quolle/mcpquolle-mcp
Python, PHP, Go and Ruby are available now. The Node.js SDK, CLI, and MCP server are in the works and publish to npm shortly — the repos are public if you want an early look.

The walkthrough below previews the Node.js SDK's interface. It's the same across every language — see each SDK's README (linked above) for idiomatic examples.

Initialize

Import Quolle and pass your API key as an object. The constructor takes { apiKey } — not a bare string.

import { Quolle } from "@quolle/sdk";

const quolle = new Quolle({ apiKey: process.env.QUOLLE_API_KEY! });

The QuolleConfig type also accepts optional baseURL (default: https://api.quolle.com) and timeout (default: 30000 ms).

Send an email

const { id } = await quolle.emails.send({
  from:    "hello@mail.yourdomain.com",
  to:      "customer@example.com",
  subject: "Welcome!",
  html:    "<h1>Thanks for signing up</h1>",
});

console.log("Queued:", id);

Send payload fields

FieldTypeRequiredNotes
fromstringYesMust be from a verified domain
tostring | string[]YesUp to 50 recipients (To + CC combined)
subjectstringYes
htmlstringYesEmail body as HTML
textstringNoPlain-text fallback
replyTostringNo
scheduledAtstringNoISO 8601 future datetime
metadataobjectNoArbitrary key/value pairs

Returns: Promise<{ id: string }>

Scheduled send

Pass a future ISO 8601 datetime in scheduledAt. The email is queued but not delivered until that time.

const { id } = await quolle.emails.send({
  from:        "hello@mail.yourdomain.com",
  to:          "customer@example.com",
  subject:     "Your weekly digest",
  html:        "<p>Here's what happened this week.</p>",
  scheduledAt: "2026-12-25T09:00:00.000Z",
});

Idempotency

Set an idempotency key before calling send. If you retry the same key, the cached response is returned and the email is not sent again. The key is scoped to your account and expires after 24 hours.

quolle.emails.setIdempotencyKey("order_invoice_12345");
const { id } = await quolle.emails.send({
  from:    "billing@mail.yourdomain.com",
  to:      "customer@example.com",
  subject: "Invoice #1234",
  html:    "<p>Your invoice is attached.</p>",
});
Use a deterministic key tied to your business event (order ID, user ID + action) so retries after a network failure don't double-send. See Conventions → Idempotency for TTL and header details.

Batch sending

Send up to 100 emails in one call. Each item in the array is a full SendEmailPayload.

const recipients = [
  { email: "alice@example.com", name: "Alice" },
  { email: "bob@example.com",   name: "Bob"   },
];

const { ids, queued } = await quolle.emails.sendBatch(
  recipients.map(r => ({
    from:    "hello@mail.yourdomain.com",
    to:      r.email,
    subject: `Hi ${r.name}, welcome!`,
    html:    `<p>Hi ${r.name}, thanks for signing up.</p>`,
  }))
);

console.log(`Queued ${queued} emails`, ids);

Returns: Promise<{ queued: number; ids: string[] }>

queued is the count of emails accepted into the queue. ids is the array of email IDs in the same order as the input.

Retrieve an email

Fetch an email by its ID to check current status, open/click counts, and timestamps. Returns a single Email object. The lookup is tenant-scoped — returns 404 if the ID doesn't belong to your account.

const email = await quolle.emails.get("a1b2c3d4-e5f6-7890-abcd-ef1234567890");

console.log(email.status);     // "queued" | "sending" | "sent" | "delivered" | "bounced" | "failed"
console.log(email.opensCount); // number of opens tracked
console.log(email.sentAt);     // ISO 8601 string, or undefined if not yet sent

Returns: Promise<Email>

FieldTypeNotes
idstringUUID
fromstring
tostringComma-joined if multiple recipients
subjectstring
statusstring"queued" | "sending" | "sent" | "delivered" | "bounced" | "failed"
opensCountnumber
clicksCountnumber
openedAtstring?ISO 8601, if opened
clickedAtstring?ISO 8601, if clicked
sentAtstring?ISO 8601, if dispatched
createdAtstringISO 8601
messageIdstring?Provider message ID
providerstring?Delivery provider used

Cancel a scheduled email

Cancel a queued email before it is dispatched. Works on emails that have not yet been handed to the delivery provider — typically scheduled sends still in the queue.

const { message } = await quolle.emails.cancel("a1b2c3d4-e5f6-7890-abcd-ef1234567890");
console.log(message); // "Email cancelled successfully"

Returns: Promise<{ message: string }>

Returns 404 if the email doesn't exist in your account. The REST equivalent is DELETE /v1/emails/:id/cancel.

Templates

Pass template (slug) and optional variables to send() instead of html. The two modes are mutually exclusive — TypeScript enforces this at compile time.

const { id } = await quolle.emails.send({
  from:      "hello@mail.yourdomain.com",
  to:        "customer@example.com",
  template:  "welcome-email",           // replace with your template's slug
  variables: { firstName: "Amaka", planName: "Starter" }, // match your template's placeholders
});

The subject is taken from the template's saved subject field. Pass subject on the payload to override it. See Templates for the full template field reference.

Error handling

The SDK throws a QuolleError when the API returns a non-2xx status. Import it alongside Quolle to branch on specific status codes.

import { Quolle, QuolleError } from "@quolle/sdk";

const quolle = new Quolle({ apiKey: process.env.QUOLLE_API_KEY! });

try {
  const { id } = await quolle.emails.send({
    from:    "hello@mail.yourdomain.com",
    to:      "customer@example.com",
    subject: "Welcome!",
    html:    "<h1>Welcome</h1>",
  });
  console.log("Queued:", id);
} catch (err) {
  if (err instanceof QuolleError) {
    console.error(err.statusCode, err.message);
    // err.data may contain extra fields (e.g. { limit, used, plan } for 402)
  }
}

See Errors for every status code, exact message strings, and handling guidance.

The same send, in every language

The interface mirrors across SDKs — here's a basic send in each:

from quolle import Quolle

quolle = Quolle("qle_...")  # or set QUOLLE_API_KEY
quolle.emails.send(
    from_="hello@mail.yourdomain.com",
    to="customer@example.com",
    subject="Welcome!",
    html="<h1>Thanks for signing up</h1>",
)
$quolle = new \Quolle\Client('qle_...'); // or QUOLLE_API_KEY
$quolle->emails->send([
    'from'    => 'hello@mail.yourdomain.com',
    'to'      => 'customer@example.com',
    'subject' => 'Welcome!',
    'html'    => '<h1>Thanks for signing up</h1>',
]);
client := quolle.New("qle_...") // or QUOLLE_API_KEY
client.Emails.Send(context.Background(), quolle.SendParams{
    From:    "hello@mail.yourdomain.com",
    To:      quolle.To("customer@example.com"),
    Subject: "Welcome!",
    HTML:    "<h1>Thanks for signing up</h1>",
})
quolle = Quolle.new(api_key: "qle_...") # or QUOLLE_API_KEY
quolle.emails.send(
  from: "hello@mail.yourdomain.com",
  to: "customer@example.com",
  subject: "Welcome!",
  html: "<h1>Thanks for signing up</h1>"
)

Full examples — templates, attachments, batch, scheduling, webhook verification — live in each SDK's README (linked in the install table above). For raw HTTP, see the API Reference.