List emails sent to the customer
const url = 'http://localhost:8080/v1/customers/example/sent-emails?limit=50&offset=0';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'http://localhost:8080/v1/customers/example/sent-emails?limit=50&offset=0' \ --header 'Authorization: Bearer <token>'Billing emails tied to this customer’s invoices from the last 30 days, newest first: invoice, payment receipt, payment failed, payment setup request, dunning, and credit note emails. Emails not tied to an invoice (such as a setup link sent from the payment-methods page) are not listed.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Query Parameters
Section titled “Query Parameters”Rows per page, from 1 to 100. Default 50. Any other value, or one
that is not a whole number, is a 422 with param: limit.
Rows to skip, 0 or more. Default 0. A negative value, or one that is
not a whole number, is a 422 with param: offset.
Responses
Section titled “Responses”Sent emails.
object
object
Hardcount’s send state. dispatched means the mail server accepted it.
What the email provider reported back, if anything.
The email in the words of the activity line standard (ADR-128),
the same as on the invoice’s payment timeline: its name and
what happened to it (“Receipt email delivered”), why it was not
sent, the invoice it was about, and who it went to. Dated at the
time on the customer’s test clock that caused it, with the real
send time as recorded_at. Present for every email type this
list returns.
object
The row’s stable key: the event’s id on an invoice timeline
row, the audit entry’s id on a subscription timeline row,
dunning: and the event’s id on a dunning run event (the same
key the invoice timeline gives that event), and email: and
the email’s id on a sent email (likewise).
Machine name of the event.
object
The meta line before the actor. Each item is one segment, shown joined by “ · “; a segment’s parts read as one phrase, for example a money part followed by the text “ refunded to card”.
Who did it. person and api_key carry the name (a person’s email
when they have no name), customer says where through via, and
automatic is anything Hardcount did on its own. Absent when no fact
written with the row names one.
object
Neutral: something happened. success: money arrived, or was returned as intended. warning: needs a look, or money will not be collected. failure: an attempt failed.
How many rows match the request’s filters, on every page, not only this one. The audit log and the customer list’s search and name or email sort say where they stop counting.
Example
{ "data": [ { "email_type": "invoice", "status": "pending", "delivery_state": "unknown", "line": { "kind": "invoice.voided", "title": "Payment failed", "amount": { "currency": "USD" }, "problem": "Card declined: insufficient funds", "detail": [ [ { "money": { "currency": "USD" }, "link": { "kind": "invoice" } } ] ], "actor": { "kind": "person", "via": "payment_page" }, "tone": "neutral" } } ]}No customer with this ID in this tenant and mode.
object
object
One of invalid_request_error, authentication_error,
rate_limit_error or api_error. Every error the API
answers has this shape, including an unknown path (404,
code route_not_found) and a method the path does not take
(405, code method_not_allowed, with an Allow header).
The few exceptions are listed in docs/api-surface.md.
The request field the error is about. Sent only when the error
names one. A request body may hold only the fields its
operation lists, spelled and cased as listed: any other
field, or one the server sets (such as id), is refused
with 422 and param naming it. An operation with no request body lists no fields: send
no body, {} or null. The Stripe and Postmark webhooks and
the LiteLLM callback take their sender’s body as it comes.
Example
{ "error": { "type": "invalid_request_error" }}limit or offset is out of range or not a whole number, or the request sends the removed after cursor (param names it)
object
object
One of invalid_request_error, authentication_error,
rate_limit_error or api_error. Every error the API
answers has this shape, including an unknown path (404,
code route_not_found) and a method the path does not take
(405, code method_not_allowed, with an Allow header).
The few exceptions are listed in docs/api-surface.md.
The request field the error is about. Sent only when the error
names one. A request body may hold only the fields its
operation lists, spelled and cased as listed: any other
field, or one the server sets (such as id), is refused
with 422 and param naming it. An operation with no request body lists no fields: send
no body, {} or null. The Stripe and Postmark webhooks and
the LiteLLM callback take their sender’s body as it comes.
Example
{ "error": { "type": "invalid_request_error" }}