tommy events API v1
The events API: everything an application under test needs to read back what it
sent.
Tommy stands in for the services an application talks to - mail providers, SMS
gateways, file transfer, chat webhooks, EDI partners - and records every message
it receives as an event. These routes list those events, fetch one in full,
stream them as they arrive, delete them, and download the bytes any of them
carried. Every event names the page that renders it, so a test can print a link
to what it just sent.
Two things are deliberately absent. The fake vendor endpoints - Mailjet's send
API, Twilio's, Slack's - are those vendors' specifications rather than tommy's,
and a partial copy of somebody else's API is worse than none; run tommy providers
for those. And each plugin's own read-back routes (/api/v1/mail/messages and its
kin) are a convenience on top of these, shaped by the content type rather than
by a contract worth generating a client from; each plugin's README documents its
own.
This document is generated from the server's route table. It cannot describe a
route that does not exist, and CI fails if the checked-in copy stops matching
the code.
Server
/api/v1 — This tommy's API.
Operations
GET
/blobs/{id}
The bytes an event carried - a mail attachment, an uploaded file - streamed with range support. Events reference these by the id in their payload rather than inlining them.
Parameters
| Name | In | Type | Description |
id required |
path |
string |
The event id, as returned by any listing. |
inline |
query |
boolean |
Serve with an inline Content-Disposition rather than as a download. |
Responses
| Status | Body | Description |
200 |
application/octet-stream
|
Success. |
400 |
application/json · api.Error
|
A query parameter could not be parsed - a malformed since, a negative limit. |
404 |
application/json · api.Error
|
No such resource, or it belongs to another plugin. |
GET
/events
Every captured event, newest first, whatever plugin captured it. Raw request bodies are omitted unless asked for: they can be megabytes each.
Parameters
| Name | In | Type | Description |
plugin |
query |
string |
Only events from this plugin, such as mail. |
provider |
query |
string |
Only entries captured by this provider, such as mailjet. |
type |
query |
string |
Only entries of this event type, such as mail.message. |
search |
query |
string |
Case-insensitive substring over the summary and type. |
since |
query |
string |
RFC3339 timestamp, a duration such as 5m, or unix milliseconds. |
limit |
query |
integer |
Maximum number of entries to return. |
offset |
query |
integer |
How many entries to skip. |
include_raw |
query |
boolean |
Include each event's raw request body. |
Responses
| Status | Body | Description |
200 |
application/json · array of ui.EventJSON
|
Success. |
400 |
application/json · api.Error
|
A query parameter could not be parsed - a malformed since, a negative limit. |
DELETE
/events
Clear captured events, all of them or one plugin's. Stored payloads deliberately survive, so a link to one already handed out keeps working.
Parameters
| Name | In | Type | Description |
plugin |
query |
string |
Clear only this plugin's events. |
Responses
| Status | Body | Description |
204 |
no body |
Done. No body. |
400 |
application/json · api.Error
|
A query parameter could not be parsed - a malformed since, a negative limit. |
GET
/events/stream
The same events as they arrive, as Server-Sent Events. Each event produces a JSON frame carrying the event without its raw body, and a frame named after the event type carrying just the id, so an htmx page can trigger on it.
Parameters
| Name | In | Type | Description |
plugin |
query |
string |
Only events from this plugin, such as mail. |
provider |
query |
string |
Only entries captured by this provider, such as mailjet. |
type |
query |
string |
Only entries of this event type, such as mail.message. |
search |
query |
string |
Case-insensitive substring over the summary and type. |
since |
query |
string |
RFC3339 timestamp, a duration such as 5m, or unix milliseconds. |
limit |
query |
integer |
Maximum number of entries to return. |
offset |
query |
integer |
How many entries to skip. |
Responses
| Status | Body | Description |
200 |
text/event-stream
|
Success. |
400 |
application/json · api.Error
|
A query parameter could not be parsed - a malformed since, a negative limit. |
GET
/events/{id}
One event in full, raw request body included.
Parameters
| Name | In | Type | Description |
id required |
path |
string |
The event id, as returned by any listing. |
Responses
| Status | Body | Description |
200 |
application/json · ui.EventJSON
|
Success. |
404 |
application/json · api.Error
|
No such resource, or it belongs to another plugin. |
DELETE
/events/{id}
Delete one captured event.
Parameters
| Name | In | Type | Description |
id required |
path |
string |
The event id, as returned by any listing. |
Responses
| Status | Body | Description |
204 |
no body |
Done. No body. |
404 |
application/json · api.Error
|
No such resource, or it belongs to another plugin. |
Schemas
api.Error
| Property | Type |
error required | string |
event.Raw
| Property | Type |
body | string, base64-encoded |
headers | object of array of string |
method | string |
path | string |
peer_addr | string |
text required | boolean |
transport required | string |
event.Summary
| Property | Type |
from | string |
snippet | string |
title | string |
to | array of string |
ui.EventJSON
| Property | Type |
id required | string |
meta | object of any |
payload | any |
plugin required | string |
provider required | string |
raw required | event.Raw |
received_at required | string (date-time) |
summary required | event.Summary |
type required | string |
url | string |