Retrieve one-shot email statistics
curl --request GET \
--url https://api.minimo.it/public/v1/emails/{id}/stats \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.minimo.it/public/v1/emails/{id}/stats"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.minimo.it/public/v1/emails/{id}/stats', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.minimo.it/public/v1/emails/{id}/stats",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.minimo.it/public/v1/emails/{id}/stats"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.minimo.it/public/v1/emails/{id}/stats")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.minimo.it/public/v1/emails/{id}/stats")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"id": "DMPNEF8",
"status": "sent",
"to": "customer@example.com",
"subject": "Your receipt",
"sentAt": "2026-05-19T10:30:00Z",
"opened": false,
"firstOpenAt": null,
"clickCount": 0,
"firstClickAt": null,
"failureReason": null
}
}Email
Get One-Shot Email Statistics
Poll delivery, open, and click metrics for a single HTML email sent via the one-shot endpoint
GET
/
public
/
v1
/
emails
/
{id}
/
stats
Retrieve one-shot email statistics
curl --request GET \
--url https://api.minimo.it/public/v1/emails/{id}/stats \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.minimo.it/public/v1/emails/{id}/stats"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.minimo.it/public/v1/emails/{id}/stats', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.minimo.it/public/v1/emails/{id}/stats",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.minimo.it/public/v1/emails/{id}/stats"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.minimo.it/public/v1/emails/{id}/stats")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.minimo.it/public/v1/emails/{id}/stats")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"id": "DMPNEF8",
"status": "sent",
"to": "customer@example.com",
"subject": "Your receipt",
"sentAt": "2026-05-19T10:30:00Z",
"opened": false,
"firstOpenAt": null,
"clickCount": 0,
"firstClickAt": null,
"failureReason": null
}
}Overview
After dispatching an email throughPOST /public/v1/emails, use the returned id (or the full statsUrl) to read back delivery and engagement metrics for that single delivery.
Unlike the template-stats endpoint, these numbers refer to one recipient and one HTML payload — not aggregated counters across a campaign.
The endpoint is scoped to your company. IDs that belong to a different company return
404, never another tenant’s
data.Request
curl --request GET \
--url https://api.minimo.it/public/v1/emails/DMPNEF8/stats \
--header 'Authorization: Bearer mn-{apiClientId}-{secret}'
Response Example
{
"data": {
"id": "DMPNEF8",
"status": "sent",
"to": "customer@example.com",
"subject": "Your receipt",
"sentAt": "2026-05-19T10:30:00Z",
"opened": true,
"firstOpenAt": "2026-05-19T10:32:14Z",
"clickCount": 2,
"firstClickAt": "2026-05-19T10:33:05Z",
"failureReason": null
}
}
Field Reference
| Field | Type | Description |
|---|---|---|
id | string | Echo of the opaque sqid id returned by the POST endpoint. |
status | "pending" | "sent" | "failed" | Lifecycle state of the delivery. |
to | string | Recipient address as it was sent. |
subject | string | Subject line as it was sent. |
sentAt | date-time | When the delivery row was created (queue-in time, not provider acceptance). |
opened | boolean | true once the tracking pixel has been fetched at least once. |
firstOpenAt | date-time | null | Timestamp of the first open; null until the pixel is fetched. |
clickCount | integer | Total number of clicks across all tracked links in the email. |
firstClickAt | date-time | null | Timestamp of the earliest tracked click; null until the first click. |
failureReason | string | null | Populated when status = "failed" — the provider’s rejection reason. |
How the Counters Are Updated
- Open: triggered the first time the recipient’s email client fetches the embedded
<img>open-pixel. Image proxies (Gmail, Apple Mail Privacy Protection) trigger this immediately on arrival, which inflates open rates compared to pre-2021 baselines. - Click: every fetch of a rewritten
/api/click/...URL appends a row to the click ledger.clickCountis the cardinality of that ledger;firstClickAtis its minimum timestamp.
id do not affect this row.
Use Cases
Confirm a magic link landed
Confirm a magic link landed
After sending a sign-in email, poll the stats endpoint to surface “Email opened by recipient” in your internal dashboard.
const { data: stats } = await fetch(`https://api.minimo.it/public/v1/emails/${id}/stats`, {
headers: { Authorization: `Bearer ${apiKey}` },
}).then((r) => r.json());
if (stats.opened) {
console.log(`Magic link opened at ${stats.firstOpenAt}`);
}
Detect failed deliveries in a queue worker
Detect failed deliveries in a queue worker
When
status === "failed", persist failureReason against the originating business object so support can react.const { data: stats } = await getEmailStats(id);
if (stats.status === 'failed') {
await logDeliveryFailure({ id, reason: stats.failureReason });
}
Attribute a CTA click back to the trigger
Attribute a CTA click back to the trigger
The first click timestamp lets you measure end-to-end latency from “we sent it” to “user acted.”
const { data: stats } = await getEmailStats(id);
if (stats.firstClickAt) {
const sentAt = new Date(stats.sentAt);
const clickedAt = new Date(stats.firstClickAt);
const minutesToFirstClick = (clickedAt - sentAt) / 60000;
track('email_click_latency_minutes', minutesToFirstClick);
}
Common Errors
| Status | Cause |
|---|---|
401 | API key missing or malformed. |
403 | API key lacks the TRANSACTIONAL permission. |
404 | The id doesn’t exist, or it belongs to a different company than the one this API key authorizes. |
Polling Guidance
- Open events: usually surface within seconds for image-proxied clients (Gmail, Apple Mail Privacy Protection). For other clients, wait until the recipient actually opens the message.
- Click events: stored synchronously the moment the rewritten URL is fetched — they appear in the next stats response.
- Rate: a steady cadence of one poll every 30–60 seconds for the first ~10 minutes is plenty. Aggressive polling won’t surface data faster.
Related Endpoints
- Send One-Shot HTML Email — the producer side of these statistics
- Get Email Template Stats — aggregated stats for template-backed sends
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Opaque sqid id returned by POST /public/v1/emails.
Example:
"DMPNEF8"
Response
Statistics for the delivery.
Show child attributes
Show child attributes