Simulators
Messages and threads
Messages and threads are the mailbox itself: the simulator keeps each message as its RFC 5322 text and renders every read from it. Read a thread, move a message through its labels, then trash and delete it and check the counts the profile reports:
curl -s -X POST "$RYSTIC_URL/_rystic/scenario" -d '{"name":"gmail_busy_inbox"}' -o /dev/null
G=(-H 'Authorization: Bearer ya29.placeholder')
curl -s "$RYSTIC_URL/gmail/v1/users/me/threads?maxResults=2" "${G[@]}" | jq -c '[.threads[] | {id, snippet}]'
# -> [{"id":"19a4c0d1e2f30004","snippet":"Standup notes - Monday: three in flight, one blocked"},{"id":"19a4c0d1e2f30003","snippet":"Your flight ZX4821 is confirmed, seat 14C"}]
curl -s "$RYSTIC_URL/gmail/v1/users/me/threads/19a4c0d1e2f30004" "${G[@]}" | jq -c '{id, messages: [.messages[] | {id, labelIds}]}'
# -> {"id":"19a4c0d1e2f30004","messages":[{"id":"19a4c0d1e2f31005","labelIds":["INBOX","UNREAD","Label_13"]},{"id":"19a4c0d1e2f31006","labelIds":["SENT","Label_13"]},{"id":"19a4c0d1e2f31007","labelIds":["INBOX","UNREAD","Label_13"]}]}
curl -s "$RYSTIC_URL/gmail/v1/users/me/messages/19a4c0d1e2f31007/modify" "${G[@]}" -H 'Content-Type: application/json' -d '{"removeLabelIds":["UNREAD"],"addLabelIds":["STARRED"]}' | jq -c '{id, labelIds}'
# -> {"id":"19a4c0d1e2f31007","labelIds":["INBOX","Label_13","STARRED"]}
curl -s -X POST "$RYSTIC_URL/gmail/v1/users/me/messages/19a4c0d1e2f31007/trash" "${G[@]}" | jq -c '{id, labelIds}'
# -> {"id":"19a4c0d1e2f31007","labelIds":["TRASH","Label_13","STARRED"]}
curl -s -X DELETE "$RYSTIC_URL/gmail/v1/users/me/messages/19a4c0d1e2f31007" "${G[@]}" -o /dev/null -w '%{http_code}\n'
# -> 204
curl -s "$RYSTIC_URL/gmail/v1/users/me/messages/19a4c0d1e2f31007" "${G[@]}" | jq -c '.error | {code, status, message}'
# -> {"code":404,"status":"NOT_FOUND","message":"Requested entity was not found."}
curl -s "$RYSTIC_URL/gmail/v1/users/me/profile" "${G[@]}" | jq -c '{emailAddress, messagesTotal, threadsTotal}'
# -> {"emailAddress":"ada.lovelace@example.com","messagesTotal":8,"threadsTotal":6}
Inserting, importing and sending
POST /gmail/v1/users/me/messages inserts a message: the whole RFC 5322 message as base64url raw, with the labelIds you give. It adds SENT when the From header is the mailbox’s own address. POST …/messages/import imports one, and POST …/messages/send sends one, labeled SENT. Each also takes an upload (Uploads), and a send records an event in the sent-mail log.
Inserted and sent messages join a thread only by the threadId you send, never by their Subject or References header. Without a threadId they start a thread of their own.
Differs from Gmail: an imported message joins no thread you can read (Limitations).
Reading a message
GET …/messages/{id} takes a format:
fullreturns the MIME part tree, each part’s headers and body, with an attachment as anattachmentId.metadatareturns only the headersmetadataHeadersnames.minimalreturns the ids, labels andsnippet.rawreturns the base64url message asraw.
Every format carries snippet and sizeEstimate. GET …/messages/{id}/attachments/{attachmentId} returns an attachment’s data and size by the id its part names. In the busy inbox, the invoice message carries a PDF and a CSV:
G=(-H 'Authorization: Bearer ya29.placeholder')
curl -s "$RYSTIC_URL/gmail/v1/users/me/messages/19a4c0d1e2f31003?format=full" "${G[@]}" | jq -c '[.payload.parts[] | {mimeType, filename}]'
# -> [{"mimeType":"text/plain","filename":""},{"mimeType":"application/pdf","filename":"invoice-4471.pdf"},{"mimeType":"text/csv","filename":"line-items.csv"}]
curl -s "$RYSTIC_URL/gmail/v1/users/me/messages/19a4c0d1e2f31003/attachments/MTlhNGMwZDFlMmYzMTAwMzoy" "${G[@]}" | jq -c '{size}'
# -> {"size":58}
Labels, trash and delete
| Request | What it does |
|---|---|
modify | adds and removes labels |
trash | adds TRASH and removes INBOX |
untrash | removes TRASH |
delete | removes the message for good; reading it then returns 404 notFound |
batchModify, batchDelete | take a list of ids and return 204 |
Threads
GET …/threads lists threads newest first, each with its snippet. GET …/threads/{id} returns every message in the thread. A thread’s modify, trash, untrash and delete act on all of its messages. A thread modify that names one label in both addLabelIds and removeLabelIds returns 400 invalidArgument, “Cannot both add and remove the same label”.
Profile
GET …/profile returns the mailbox’s emailAddress and its current historyId, with messagesTotal and threadsTotal as they stand after the last write.
Errors
Every JSON error is Gmail’s envelope: {"error": {"code", "message", "status", "errors": [{"domain", "reason", "message"}]}}.
| Request | Returns |
|---|---|
| an unknown id | 404 notFound, “Requested entity was not found.” |
a userId the simulator does not take | 403 forbidden (Auth) |