← Back to resources

Builder resources

Get started with the Qsendyx API

From signing up to a delivered message: authenticate a domain, create a token, send your first email and receive the status webhook.

12 min read

This is the shortest path from an empty account to a message that actually lands in an inbox. Follow it in order — each step unlocks the next, and skipping domain authentication is the single most common reason a first send never arrives.

Before you start

You need three things: an account, a sending identity you control, and a token. Everything else in this guide is a consequence of those three.

  • An email domain you can edit DNS for — a subdomain such as mail.yourcompany.com is the recommended setup.
  • A terminal with curl, or any HTTP client. There is no SDK requirement: the API is plain REST over JSON.
  • Roughly ten minutes, plus DNS propagation time, which is out of anyone’s control.
Transactional and marketing traffic are separated from the schema up. Decide which one you are building before you send — mixing the two on the same identity contaminates your reputation, and reputation does not roll back.

1. Create your account

Sign up and you get a tenant with an isolated project, a wallet and a test environment. Nothing is billed until you send: pricing is per message, and the panel shows the estimated cost of every request before it leaves the queue.

A new account starts in the test environment. Test tokens are prefixed qsy_test_ and never reach a real inbox — they exercise the full pipeline, including webhooks, without spending balance.

2. Authenticate your sending domain

Mailbox providers do not care who you are; they care whether the domain in the From address vouches for the server that sent the message. That proof is DNS, and it is not optional.

curl -X POST https://api.qsendyx.com/api/v1/email/domains \
  -H "Authorization: Bearer $QSENDYX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "mail.yourcompany.com" }'
Registers the domain and returns the records to publish.
  1. 1 SPF Authorises our infrastructure to send on behalf of the domain. One SPF record per domain — a second one invalidates both.
  2. 2 DKIM Signs each message with a private key so the recipient can verify it was not altered in transit. We generate the key pair; you publish the public half.
  3. 3 DMARC Tells providers what to do when SPF and DKIM fail, and where to send the reports. Start at p=none, read the reports for a couple of weeks, then tighten.

Publish the records, then trigger verification. Propagation usually takes minutes, sometimes hours. Verification is idempotent: run it as often as you like.

Prefer a dedicated subdomain for sending. If a campaign ever damages the reputation of mail.yourcompany.com, your corporate email on yourcompany.com is untouched.

3. Create an API token

Tokens carry scopes, and scopes are the blast radius of a leak. A token that only needs to send email should never be able to read your billing or manage webhooks.

ScopeGrants
email:sendSend email, including template-based sends
email:readRead message status and events
email:domainsRegister and verify sending domains
sms:sendSend SMS
sms:readRead SMS status and events
webhooks:manageCreate, rotate and inspect webhook endpoints
analytics:readRead aggregate delivery metrics
The secret is shown once, at creation. We store only a prefix, a hash and the last characters — we cannot recover it for you, by design. If you lose it, revoke and create another.
Authorization: Bearer qsy_live_91fb5d2dc31fd065...
Every request carries the token as a bearer.

4. Send your first message

One POST. The from address must belong to a verified domain or a confirmed sender, otherwise the request is rejected before anything is charged.

curl -X POST https://api.qsendyx.com/api/v1/email/messages \
  -H "Authorization: Bearer $QSENDYX_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "from":    { "email": "you@yourcompany.com", "name": "Your Company" },
    "to":      [{ "email": "user@example.com" }],
    "subject": "Welcome aboard",
    "html":    "<h1>You are in.</h1>"
  }'
{
  "id": "019f7141-46ac-76c3-8276-042f38c68cd8",
  "status": "queued",
  "channel": "email",
  "to": "user@example.com",
  "from": "you@yourcompany.com",
  "type": "transactional",
  "created_at": "2026-07-17T18:05:31Z",
  "estimated_cost_micros": 0,
  "currency": "USD"
}
202 Accepted — queued, not yet delivered.

A 202 means we took responsibility for the message, reserved the balance and put it on a queue. Delivery is asynchronous by design: an SMTP conversation with a remote provider can take seconds, and no API should hold your request open for that.

5. Track what happened

Two ways to find out how a message ended, and you should use both. Polling answers "what about this one?"; webhooks answer "tell me when anything changes".

curl https://api.qsendyx.com/api/v1/email/messages/019f7141-46ac-76c3-8276-042f38c68cd8 \
  -H "Authorization: Bearer $QSENDYX_TOKEN"

The lifecycle is queued → sent → delivered, with bounced, failed, rejected and suppressed as terminal alternatives. Engagement events — opened, clicked, unsubscribed — arrive after delivery and do not change the delivery status.

Do not treat a missing delivered as a failure and resend. Provider events arrive out of order and duplicated as a rule, not as an exception — that is why every message carries a stable id.

6. Receive events on your endpoint

Register an HTTPS endpoint and we POST every event you subscribed to, signed with a per-endpoint secret. Respond 2xx quickly and do the real work asynchronously — a slow endpoint gets retried, and retries look like duplicates.

{
  "id": "evt_3f9a1c72b0d84e15a6c27f31",
  "type": "message.delivered",
  "version": "1",
  "occurred_at": "2026-07-17T18:05:36.918Z",
  "tenant_id": "019f70a4-3c8e-7d21-b0f4-51ac9e77b201",
  "project_id": "019f70a4-3c8e-7d21-b0f4-51ac9e77b3aa",
  "resource_id": "019f7141-46ac-76c3-8276-042f38c68cd8",
  "data": {
    "message_id": "019f7141-46ac-76c3-8276-042f38c68cd8",
    "status": "delivered",
    "channel": "email",
    "message_type": "transactional",
    "recipient": "user@example.com"
  }
}
The event envelope.

Verify the signature before you trust a single field. The HMAC covers the timestamp and the raw body, so a captured request cannot be replayed against you forever.

import crypto from "node:crypto";

// O corpo BRUTO, antes de qualquer JSON.parse — reserializar muda os bytes e quebra o HMAC.
export function verify(rawBody, headers, secret) {
  const timestamp = headers["x-webhook-timestamp"];
  const signature = headers["x-webhook-signature"];

  // Sem a janela de tolerância, um webhook capturado é replicável para sempre.
  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!timestamp || age > 300) return false;

  const expected =
    "v1=" +
    crypto
      .createHmac("sha256", secret)
      .update(`${timestamp}.` + rawBody)
      .digest("hex");

  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

7. Go live

Swap the test token for a live one and keep an eye on the first few thousand messages. Volume earns reputation; volume that arrives all at once earns suspicion.

  • Ramp gradually. A domain that sent nothing yesterday and fifty thousand today looks exactly like a compromised account.
  • Watch bounces and complaints, not just deliveries. A hard bounce rate above a couple of percent is a list-quality problem, and it is being counted against you.
  • Honour unsubscribes immediately — we suppress them automatically, and a suppressed recipient is never sent to again.
  • Keep transactional and marketing on separate identities. One bad campaign should not take your password resets down with it.

Keep going