Questions · Updated 2026-09-28
How do I tag emails and find them later?
Tag emails by passing tags on POST /emails, a string map such as {"campaign": "welcome"}. Tags come back on GET /emails/{id} and inside data on every webhook, which is how you find them later. GET /emails filters by status, sender, recipient, domain, key and template; it has no tag filter.
Tag emails by passing tags in the body of POST /emails: a string map such as {"campaign": "welcome"}, or an array of {name, value} objects. Both are stored as the map. The tags come back on GET /emails/{id} and inside data on every email.* webhook payload, and that is how you find them later. GET /emails filters by status, sender, recipient, domain, key and template, and has no tag filter today, so a tag is for the handler that receives the event, not for the list query.
Rules for a tag
An email carries up to 75 tags; more are refused with invalid_parameter and a message naming the count and the limit. Names and values are tokens: the refusal for anything else says a tag "must use only ASCII letters, numbers, underscores and dashes (max 256 chars) in both name and value." A control character in a name or value is refused before that, with "Tag name contains a control character." or its value counterpart. None of these refusals is retryable, so change the tag and send again. In POST /emails/batch, tags belongs to each item, so two items in one call can carry different tags.
Where a tag travels
GET /emails/{id} returns tags as the map, whichever shape you sent. Every webhook payload for an email.* event carries email_id and tags in data, with {} when the message had none, so a handler for email.bounced can read data.tags.campaign and act without a second request. That is the mechanism to use for "what happened to the welcome campaign": subscribe an endpoint with POST /webhooks, and count outcomes by tag as the events arrive. Tags do not appear in GET /emails/{id}/events rows; they sit on the message and on the payload.
curl -sS -X GET https://api.agentisend.com/emails \
-H "Authorization: Bearer $AGENTISEND_API_KEY"200
{
"data": [],
"has_more": true,
"next_cursor": "string",
"object": "string"
}Finding messages
GET /emails is the list, and its summary says "Every console filter is a query param here". The filters are status (one status, or several comma-separated), q, from, to, domain, bounce_class (hard, soft, block or policy, which implies status=bounced), api_key_id, template_id, since and until. Page with limit, up to 100, and pass the reply's next_cursor back as cursor while has_more is true. A cursor from a page you no longer hold is refused with invalid_cursor, whose fix is "Call the list again without a cursor, or pass the next_cursor from a page you still have." GET /emails/export.csv takes the same filters and returns the rows as a file.
Because the list filters on key and template rather than on tag, put the dimension you will query on into one of those: one API key per agent or per service makes api_key_id the filter for "everything this agent sent", and one template per message type makes template_id the filter for "every receipt". Keep tags for the values a webhook handler needs at the moment the event arrives.