List messages
GET/messagesBearer token required
Summaries of the messages in your namespace, newest first. This is a debugging tool — for reading a code in a test, use GET /messages/latest instead.
Parameters
| Name | Default | Meaning |
|---|---|---|
addressoptional | all yours | Restrict to one address. Omit it to see the whole namespace |
limitoptional | 50 | How many to return, between 1 and 200 |
Example
import os, requests
reply = requests.get(
"https://api.ravenhook.dev/messages",
params={"limit": 20},
headers={"Authorization": f"Bearer {os.environ['RAVENHOOK_TOKEN']}"},
)
for summary in reply.json():
print(summary["to"], summary["subject"])const res = await fetch(`https://api.ravenhook.dev/messages?limit=20`, {
headers: { Authorization: `Bearer ${process.env.RAVENHOOK_TOKEN}` },
});
const messages = await res.json();
for (const summary of messages) {
console.log(summary.to, summary.subject);
}curl -s -G "https://api.ravenhook.dev/messages" \
--data-urlencode "limit=20" \
-H "Authorization: Bearer $RAVENHOOK_TOKEN"Response
Summaries only — no bodies. Fetch one with GET /messages/{id} when you need its content.
[
{
"id": "ed32433d12e145b19ee0117418d688b6",
"subject": "Your verification code",
"from": "noreply@theircompany.com",
"to": "mfa@ex4mple0ns.inbox.ravenhook.dev",
"message_id": "",
"received_at": 1785515448.6,
"has_attachments": false
}
]There is no pagination
limit caps at 200 and there is no offset or cursor, so an unfiltered call returns the newest 200 of the up to 500 stored in your namespace. The older ones are not reachable from this call.
They are not lost. A single address holds at most 100 messages, which is below the 200 cap, so ?address= always returns everything stored for that address. In a test you know which address you used, so nothing you care about is out of reach.
There is also no search. You cannot query by subject, sender or body — filter by address, then match on the summaries yourself.
Reaching a specific message
Three calls, and the second is the one that matters:
import os, requests
API = "https://api.ravenhook.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['RAVENHOOK_TOKEN']}"}
def summaries(address=None, limit=200):
params = {"limit": limit}
if address:
params["address"] = address
return requests.get(f"{API}/messages", params=params, headers=HEADERS).json()
# 1. the newest 200 across the whole namespace
recent = summaries()
# 2. everything stored for one address - reaches messages that
# the unfiltered call above cannot show you
mine = summaries("mfa@ex4mple0ns.inbox.ravenhook.dev")
# 3. pick one and fetch it in full
wanted = next(s for s in mine if "verification" in s["subject"].lower())
message = requests.get(f"{API}/messages/{wanted['id']}", headers=HEADERS).json()
print(message["text_body"])const API = "https://api.ravenhook.dev";
const headers = { Authorization: `Bearer ${process.env.RAVENHOOK_TOKEN}` };
async function summaries(address, limit = 200) {
const params = new URLSearchParams({ limit: String(limit) });
if (address) params.set("address", address);
const res = await fetch(`${API}/messages?${params}`, { headers });
return res.json();
}
// 1. the newest 200 across the whole namespace
const recent = await summaries();
// 2. everything stored for one address - reaches messages that
// the unfiltered call above cannot show you
const mine = await summaries("mfa@ex4mple0ns.inbox.ravenhook.dev");
// 3. pick one and fetch it in full
const wanted = mine.find((s) => /verification/i.test(s.subject));
const res = await fetch(`${API}/messages/${wanted.id}`, { headers });
const message = await res.json();# 1. the newest 200 across the whole namespace
curl -s -G "https://api.ravenhook.dev/messages" --data-urlencode "limit=200" -H "Authorization: Bearer $RAVENHOOK_TOKEN"
# 2. everything stored for one address
curl -s -G "https://api.ravenhook.dev/messages" --data-urlencode "address=mfa@ex4mple0ns.inbox.ravenhook.dev" -H "Authorization: Bearer $RAVENHOOK_TOKEN"
# 3. fetch one in full, using an id from either listing
curl -s "https://api.ravenhook.dev/messages/ed32433d12e145b19ee0117418d688b6" -H "Authorization: Bearer $RAVENHOOK_TOKEN"Step 2 is how you reach anything the unfiltered listing cannot show. Because a single address stores at most 100 messages and the cap is 200, that call is never truncated — whatever is still stored for that address comes back, whether it is the 500th newest in your namespace or the first.
Which is why per-run addresses are worth the two lines they cost. A test that invents its own address never has to search at all: everything at that address belongs to that run.
Two different ids
id | Ours. Stable, always present, and what you pass to the single-message and delete endpoints |
message_id | Whatever the sender put in the email's Message-ID header. It can be anything, or empty. Do not rely on it |
Finding out which addresses received mail
There is no endpoint that lists your addresses, because addresses are never created and so cannot be enumerated. An address is not an object — it is any local part you choose, valid the moment mail arrives for it.
To see which ones have actually received something, read the to field here. That is the real answer to "what inboxes do I have?": the ones mail has arrived at.
Errors
| Code | Cause |
|---|---|
400 | Malformed address |
401 | Token missing, malformed, or unknown |
404 | Address outside your namespace |
422 | limit outside 1 to 200 |
429 | Too many requests |