Simulators
Uploads
Gmail’s client libraries send a message as an upload when it carries a media body, so the simulator serves the /upload/gmail/v1 routes beside /gmail/v1. Insert a message as raw message/rfc822 bytes, then as a multipart body with its labels, send one and meet the size cap:
curl -s -X POST "$RYSTIC_URL/_rystic/scenario" -d '{"name":"gmail_busy_inbox"}' -o /dev/null
G=(-H 'Authorization: Bearer ya29.placeholder')
MSG=$'From: ada.lovelace@example.com\r\nTo: marcus.bell@northwind.example\r\nSubject: Uploaded\r\n\r\nBy upload.\r\n'
printf '%s' "$MSG" | curl -s "$RYSTIC_URL/upload/gmail/v1/users/me/messages?uploadType=media" "${G[@]}" -H 'Content-Type: message/rfc822' --data-binary @- | jq -c '{labelIds}'
# -> {"labelIds":[]}
printf -- '--b\r\nContent-Type: application/json\r\n\r\n{"labelIds":["INBOX","UNREAD"]}\r\n--b\r\nContent-Type: message/rfc822\r\n\r\n%s\r\n--b--\r\n' "$MSG" | curl -s "$RYSTIC_URL/upload/gmail/v1/users/me/messages?uploadType=multipart" "${G[@]}" -H 'Content-Type: multipart/related; boundary=b' --data-binary @- | jq -c '{labelIds}'
# -> {"labelIds":["INBOX","UNREAD"]}
printf '%s' "$MSG" | curl -s "$RYSTIC_URL/upload/gmail/v1/users/me/messages/send?uploadType=media" "${G[@]}" -H 'Content-Type: message/rfc822' --data-binary @- | jq -c '{labelIds}'
# -> {"labelIds":["SENT"]}
curl -s "$RYSTIC_URL/gmail/v1/users/me/messages?labelIds=UNREAD&q=subject:uploaded" "${G[@]}" | jq -c '{resultSizeEstimate}'
# -> {"resultSizeEstimate":1}
curl -s "$RYSTIC_URL/_rystic/version" | jq -c '{blob_cap_bytes}'
# -> {"blob_cap_bytes":1048576}
head -c 1100000 /dev/zero | tr '\0' 'a' | curl -s "$RYSTIC_URL/upload/gmail/v1/users/me/messages?uploadType=media" "${G[@]}" -H 'Content-Type: message/rfc822' --data-binary @- -o /dev/null -w '%{http_code}\n'
# -> 413
Which routes take an upload
Insert, import and send take an upload, and so do a draft’s create, update and send. Both forms are served on each route:
uploadType=mediacarries themessage/rfc822bytes alone.uploadType=multipartcarries a JSON metadata part, then the message, in onemultipart/relatedbody.
A media drafts.send carries no draft id and returns 400 invalidArgument, “Invalid draft”. A multipart one takes the id in its metadata part (Drafts).
Differs from Gmail: an insert by media upload returns a snippet member that Gmail leaves out (Limitations).
Differs from Gmail: an import by upload returns a labelIds member beside its id, where Gmail returns the id alone (Limitations).
The size cap
An upload’s body holds up to 1 MiB, and a larger one returns 413. GET /_rystic/version reports the cap as blob_cap_bytes. A message sent as base64url raw in JSON has no such cap.
Differs from Gmail: Gmail takes an upload of up to 150 MB on insert and import, but only 35 MB on send and the draft routes.