Tags
Label your sends to filter the email log and route webhook events by category, customer, or anything else you track.
Tags are key-value labels you attach to a send. They exist for two jobs:
- Filtering your log. "Show me every password-reset send from the last 7 days" is one
click in the dashboard, or one query parameter on
GET /emails. - Routing webhooks. Every
email.*event for a send carries its tags, so your handler can branch onpayload.tags.categoryinstead of looking the email up first.
Adding tags to a send
Pass tags as an object:
curl -X POST https://api.eusend.dev/emails \
-H "Authorization: Bearer eu_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Reset your password",
"html": "<p>Here is your reset link.</p>",
"tags": {
"category": "password_reset",
"tier": "pro"
}
}'The [{ "name": ..., "value": ... }] array form works identically, so a payload written
against Resend's API sends here unchanged:
{
"tags": [
{ "name": "category", "value": "password_reset" },
{ "name": "tier", "value": "pro" }
]
}Responses and webhook payloads always return the object form.
Tags work on POST /emails and on every item of POST /emails/batch.
| Parameter | Type | Description |
|---|---|---|
Maximum tags per email | 10 | Applies to each item of a batch send independently. |
Allowed characters | A–Z a–z 0–9 _ - | Names and values alike. No spaces, colons, dots, or non-ASCII characters — a value like an email address or a timestamp will be rejected, so hash or slugify those first. |
Maximum name length | 64 characters | |
Maximum value length | 256 characters |
Tags are for grouping sends, not for carrying data. Something high-cardinality — a user ID, an order number — makes a filter that matches exactly one email, which the email ID already does better. Tag the kind of mail, and keep the identifier in your own system.
Filtering the log
In the dashboard, tags render as chips on the email list and detail pages. Clicking one filters the log to it.
Over the API, pass tag to GET /emails:
# every send tagged category=password_reset
curl "https://api.eusend.dev/emails?tag=category:password_reset" \
-H "Authorization: Bearer eu_live_xxxxxxxxxxxx"
# every send carrying a category tag at all, whatever its value
curl "https://api.eusend.dev/emails?tag=category" \
-H "Authorization: Bearer eu_live_xxxxxxxxxxxx"
# repeat the parameter to require several — this matches both
curl "https://api.eusend.dev/emails?tag=category:welcome&tag=tier:pro" \
-H "Authorization: Bearer eu_live_xxxxxxxxxxxx"tag combines with the other filters (status, from, to) and with cursor pagination.
Tags in webhooks
Every email.* event carries the sending email's tags, as an object. Sends made without
tags carry "tags": {} rather than omitting the field, so a handler can read
payload.tags.category without guarding first.
{
"type": "email.delivered",
"email_id": "9a8b7c6d-5e4f-4a3b-8c1d-0e9f8a7b6c5d",
"recipients": ["[email protected]"],
"tags": { "category": "password_reset", "tier": "pro" },
"timestamp": "2026-08-06T10:24:31.000Z"
}This makes a single webhook endpoint enough for several kinds of mail:
app.post('/webhooks/eusend', (req, res) => {
const { type, tags } = req.body;
if (type === 'email.bounced' && tags.category === 'password_reset') {
// A reset mail that bounced is a locked-out user — page someone.
alertOnCriticalBounce(req.body);
}
res.sendStatus(200);
});Tags over SMTP
If you relay through the SMTP bridge rather than the API, add an X-Eusend-Tag header.
It may repeat, and one header may carry several comma-separated pairs:
X-Eusend-Tag: category=password_reset
X-Eusend-Tag: tier=pro, region=euPairs that don't fit the character rules are dropped and the message still sends — unlike the API, SMTP has no useful way to hand back a validation error, and a bounced invoice is a worse outcome than an untagged one.