Use case · Verification codes

Email OTP and verification codes, without the tracking.

tinyRelay is a verification email API and SMTP relay for one-time codes. Send each code with a single request, turn tracking off for that request with disableTrackingLinks, and get a signed DELIVERED or BOUNCED event back.

"disableTrackingLinks": true
Links not rewrittenNo open pixel
SENDDELIVEREDno OPEN or CLICK events
// disableTrackingLinks left out: tracking on by default
Links rewrittenOpen pixel added
SENDDELIVEREDOPENCLICK

Example email and codes · illustrative

How it works

A 2FA email in three moves.

Your app owns the code. tinyRelay owns the delivery, and tells you how it went.

1

Your app makes the code

Generate it, store a hash with an expiry, and rate-limit how often a user can ask. None of that touches the email service.

2

One request sends it

POST /email/campaign with the code in the recipient's attributes and disableTrackingLinks: true. A 202 returns a requestId.

3

An event comes back

DELIVERED when the receiving server accepts it, BOUNCED with a category if it does not. Your user ID rides along as metadata.

Tracking

Why sign-in code emails go out with tracking off.

Open and click tracking are on by default. For a code email they add little and change what you send.

  • With tracking on, every link is rewritten through a click URL and an open pixel is added. A magic sign-in link in a code email is rewritten too.
  • Without link wrapping, the rewritten link exposes the destination as a readable redirectUrl parameter.
  • A CLICK event's url carries the full original link, query string included, so a sign-in token in the link ends up in your webhook data.
  • With disableTrackingLinks: true, links are not rewritten and no pixel is inserted. One switch covers both, for the whole request.

Keep tracking on for your other email if you want OPEN and CLICK events there. The setting is per request, so code emails and receipts can differ.

Try the switches

per request

See what happens to a link in the email as you change the request.

Open + click tracking (off = disableTrackingLinks: true)
Link wrapping (X-Link-Wrapping: true)
THE LINK AS SENT

The example uses a reset-style link; the same rewriting applies to any link in a code email. [tracking-domain] stands in for the real tracking host.

The request

Verification email API request, per code.

The code goes in attributes and is merged into the body with a [CODE] tag. Attributes are never echoed on webhooks. Metadata is echoed on every event, so it carries the user ID and never the code.

attributes
In the email

Rendered into the body. Not sent back to you.

metadata
In every event

Never rendered. Up to 10 keys, echoed on each webhook.

disableTrackingLinks in the Send Email docs →
curl -X POST https://api.tinyemail.com/v1/relay/email/campaign \
  -H "X-RELAY-ACCESS-TOKEN: $TINYRELAY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "Your Acme sign-in code",
    "preview": "Enter this code to finish signing in.",
    "body": "<p>Your sign-in code is <strong>[CODE]</strong>. It works once.</p>",
    "sender": { "from": { "name": "Acme", "email": "no-reply@acme.com" } },
    "recipients": { "to": [
      { "email": "ann@example.com", "attributes": { "CODE": "482913" } }
    ] },
    "metadata": { "user_id": "u_1042", "flow": "sign_in_code" },
    "disableTrackingLinks": true
  }'
import os, requests

resp = requests.post(
    "https://api.tinyemail.com/v1/relay/email/campaign",
    headers={"X-RELAY-ACCESS-TOKEN": os.environ["TINYRELAY_KEY"]},
    json={
        "subject": "Your Acme sign-in code",
        "preview": "Enter this code to finish signing in.",
        "body": "<p>Your sign-in code is <strong>[CODE]</strong>.</p>",
        "sender": {"from": {"name": "Acme", "email": "no-reply@acme.com"}},
        "recipients": {"to": [{"email": user.email, "attributes": {"CODE": code}}]},
        "metadata": {"user_id": user.id, "flow": "sign_in_code"},
        "disableTrackingLinks": True,
    },
)
# 202 → {"requestId": "…", "message": "SUCCESS"}
request_id = resp.json()["requestId"]

Base URL from current site samples; confirm in the docs.

What comes back

Know when the code landed, or why it did not.

With tracking off there is no pixel to fire OPEN and no rewritten link to fire CLICK. Plan your code flow around SEND, DELIVERED and BOUNCED.

All seven webhook events →
  • SENDSent to the receiving serverstart the timer
  • DELIVEREDThe receiving server accepted it"check your inbox"
  • BOUNCEDSOFT_BOUNCED, e.g. category 22, mailbox fulloffer another way
  • BOUNCEDHARD_BOUNCED, category 10, bad addressask for a new address

Every event is signed with svix-id, svix-timestamp and svix-signature, and carries your metadata.

FAQ

Email OTP questions.

Short answers here; the request fields are in the email API overview.

Can I turn off tracking for code emails only?

Yes. Tracking is set per request. Send your code emails with disableTrackingLinks set to true and leave it out of your other requests. It turns off both link rewriting and the open pixel for that whole request.

No. disableTrackingLinks is a single switch that covers both. There is no documented way to disable one and keep the other, and it cannot be set per recipient.

In the recipient's attributes, rendered into the body with a merge tag such as [CODE]. Attributes are never echoed on webhooks. Metadata is echoed on every event, so keep codes out of it.

No. Your app generates the code, stores it, expires it and checks it. tinyRelay delivers the email and reports what happened to it.

A DELIVERED event tells you the receiving server accepted the message. If it bounced, a BOUNCED event tells you whether it was hard or soft, with one of 19 documented categories.

Get started

Send transactional email with a service that reports back.

SMTP relay or REST API, standalone or alongside tinyEmail. Tell us what you send and our team sets you up.