Get an API key

Reference

Account and usage

Three reads: what this credential is and what it may do, what you have sent this month, and whether the delivery record itself is still intact.

Who am I

GET/v1/meaccount:read

The first call worth making with a new key: it confirms the key works, tells you which account it belongs to, and lists exactly the scopes it carries — which is faster than discovering a missing scope from a 403 on your first send.

curl https://api.posthastemail.dev/v1/me \
  -H "authorization: Bearer $POSTHASTE_KEY"

# 200
# {
#   "id": "acct_Qp4Ns8Vd1Lr6Ta0YhJ3mWc",
#   "name": "Acme Ltd",
#   "slug": "acme-ltd",
#   "status": "active",
#   "environment": "live",
#   "scopes": ["account:read", "emails:send", "messages:read"],
#   "plan": {
#     "id": "starter",
#     "name": "Starter",
#     "monthlyEmails": 100000,
#     "maxDomains": null,
#     "retentionDays": 90,
#     "dedicatedIp": false
#   },
#   "subscription": {
#     "status": "active",
#     "graceEndsAt": null,
#     "cancelAtPeriodEnd": false
#   },
#   "sending": {
#     "dailyLimit": 2500,
#     "sentToday": 418,
#     "remainingToday": 2082,
#     "warmupTier": 3,
#     "sentThisMonth": 21044,
#     "remainingThisMonth": 78956
#   }
# }
FieldNotes
id / name / slugThe account. slug is stable and is not changed when the account is renamed, so it is safe to store.
statusactive, suspended or closed. A non-active account cannot authenticate at all, so in practice this reads active.
environmentlive or test, from the key. A label only — both send real mail.
scopesExactly what this credential may do. Worth asserting against at start-up rather than at 3am.
plan{ id, name, monthlyEmails, maxDomains, retentionDays, dedicatedIp }. maxDomains is null when unlimited.
subscription{ status, graceEndsAt, cancelAtPeriodEnd }, or null when there is nothing to say — a banner that appears for every account with no subscription is noise that trains people to ignore the one that matters.
sendingdailyLimit, sentToday, remainingToday, warmupTier, sentThisMonth, remainingThisMonth. Everything needed to avoid a 429 rather than react to one.
StatusWhen
200The account.
403 forbiddenThe key lacks account:read.
404 not_foundThe account no longer exists.

Usage for the current month

GET/v1/usageaccount:read

Totals for the current UTC calendar month, with the daily series behind them. The series ships alongside the total deliberately: a customer questioning a number should be able to see which days produced it without asking us.

curl https://api.posthastemail.dev/v1/usage \
  -H "authorization: Bearer $POSTHASTE_KEY"

# 200
# {
#   "periodStart": "2026-08-01",
#   "periodEnd": "2026-08-31",
#   "sent": 21044,
#   "delivered": 20781,
#   "bounced": 212,
#   "complained": 4,
#   "deliveryRate": 0.98750,
#   "complaintRate": 0.00019,
#   "bounceRate": 0.01007,
#   "days": [
#     { "day": "2026-08-01", "sent": 702, "delivered": 694,
#       "bounced": 7, "complained": 0 },
#     …
#   ]
# }
FieldNotes
periodStart / periodEndFirst and last day of the current UTC month, as YYYY-MM-DD.
sentAccepted messages. This is the billable figure. A message we refused was never attempted, so it is never counted.
delivered / bounced / complainedOutcomes so far. Mail sent late in the month may still be in flight.
deliveryRatedelivered / sent.
complaintRatecomplained / delivered — measured against delivered mail, which is what a receiving provider measures.
bounceRatebounced / sent — measured against what we attempted.
days[]One row per day that had activity, oldest first. Days with nothing on them are absent here — unlike /v1/stats/messages, which fills them in with zeroes for charting.

The rates here are fractions, not percentages. A complaint rate of 0.00019 is 0.019%. The rates on /v1/stats/messages are percentages — 0.02 there means the same thing. Multiplying the wrong one by 100 is how a healthy account ends up on an alerting dashboard in red.

The counters are written by a trigger on the event log rather than tallied separately, so usage is a projection of the append-only record. Every unit counted has an event behind it, which is what makes the number defensible when somebody queries a bill.

Verify the whole record

GET/v1/account/verifyaccount:read

Replays your account’s entire event chain, in order, and reports the first break. This is the strong version of the tamper-evidence claim: not “these two rows still hash correctly” but “nothing in your whole delivery history has been altered or removed”.

curl https://api.posthastemail.dev/v1/account/verify \
  -H "authorization: Bearer $POSTHASTE_KEY"

# 200 — the record is intact
# {
#   "intact": true,
#   "brokenAt": null,
#   "problem": null,
#   "checked": "every event on this account, in order"
# }

# 200 — it is not. This is evidence, not an outage.
# {
#   "intact": false,
#   "brokenAt": 40912,
#   "problem": "hash mismatch",
#   "checked": "every event on this account, in order"
# }
FieldNotes
intactWhether the chain verifies end to end.
brokenAtThe sequence number at which the record stops being trustworthy, or null. It is a position, not an identifier.
problemWhat went wrong — a hash mismatch, a sequence gap — or null.
checkedA plain statement of what was verified, so the answer cannot be over-read.

This is the account-wide check. The per-message equivalent is recordIntact on GET /v1/messages/:id, which proves those events’ contents are unaltered but cannot prove nothing was deleted — the events either side of a message’s own belong to other messages.

The work happens in the database rather than by shipping every event to a process, because “every event” is the point and an account can have millions of them. Expect it to take longer than the other reads on a large account.

Keys

GET /v1/api-keys lists this account’s credentials, including revoked ones. It is documented with the rest of authentication.

NextBillingSubscription state, the commercial history, and downloadable invoices.