Skip to main content

Messages

Your tool can tell someone in the course that something happened: the instructor that an essay is ready to grade, a student that their feedback is in. You say what and to whom; Voshi delivers it. Today that is an email, from "Your Tool via Voshi". How it is delivered is Voshi's choice and may grow — your code is the same either way.

async submitEssay(essay) {
await this.api.storage.memberLocation.set({ essay, submitted: true })
if (this.instructorId) {
try {
await this.api.notifyMember(this.instructorId, {
subject: 'An essay is ready to grade',
body: `<p><voshi-member mid="${this.api.user.member}"></voshi-member> submitted an essay.</p>`,
})
} catch (err) {
console.error(err) // the essay is saved; the message is a courtesy
}
}
}

Ask for it in voshi.json​

A tool sends messages only to the kinds of people its manifest asks for, each with a sentence saying why:

{
"manifest_version": 2,
"app": { "entry": "app.js", "tag": "my-tool" },
"notify": [
{ "recipient": "instructor", "reason": "Tells the instructor when an essay is ready to grade." },
{ "recipient": "student", "reason": "Tells a student when their essay has been graded." }
]
}

instructor is the course staff: instructors, assistants, and managers. student is every other member of the course. Messaging students is the bigger ask, so ask for it only when the tool does it. The ask is part of the version and is reviewed with it.

Who can message whom​

The launch isIt may message
Course staffAny member of the course
A student, or a mentorThemselves and the course staff

Both rules apply: an instructor's launch messaging a student needs student in the manifest, and a student's launch messaging their own instructor needs instructor. Find the instructor with listMembers(): the members whose isStaff is true.

To decide what to offer before sending anything, read api.launch_data.notify: its recipients lists the kinds the manifest asks for ('instructor', 'student'), and it is null when the manifest asks for none.

notifyMember(mid, message)​

midstringrequired

The member to message: api.user.member, or a member's id from listMembers().

subjectstringrequired

Plain text, at most 200 characters. Line breaks become spaces.

bodystringrequired

The message, as an HTML fragment of at most 20 KB. See The body.

It resolves to the recorded attempt, a notification event: status is success when the message was delivered and failed when it could not be (the member has no email address on record, say). It does not say how it was delivered. Every attempt also appears on the dashboard's Events tab.

The body​

The body is HTML with formatting only:

Allowed
Textp, br, h3, h4, blockquote
Emphasisstrong, b, em, i, u
Listsul, ol, li
Linksa with an href, and no other attribute
Names<voshi-member>, as on the page

Anything else — a div, an img, a class or style attribute — is refused with a message naming it, and nothing is sent. Plain text is a body too, but it is read as HTML, so escape < and & in text you did not write, such as a location label or a student's answer.

Names. Write a member's name or email with the same <voshi-member> element you place on the page. Voshi fills it in for the recipient, as the recipient would see it there: the manifest's members must ask for the field, and the recipient must be allowed to see the member. A student cannot send themselves another student's email; the message is refused instead.

Links. A link may point to your tool's own files (api.assets.url('guide.pdf')) or to a URL the manifest's external lists. mailto: links are always fine. A message is mailed under Voshi's name, so a link anywhere else is refused.

No images. An image in an email tells whoever serves it when the message was opened, so messages carry none.

Limits​

A tool may send at most 20 messages a day to one member, and 500 a day in one course. Past either, notifyMember() throws a VoshiRateLimitError. Send a message for something a person should act on, not for every click.

Errors​

ErrorWhen
VoshiValidationErrorThe subject or body was refused: a tag, attribute, or link that is not allowed, a <voshi-member> the recipient may not see. err.message names it.
VoshiPermissionErrorThe manifest does not ask to message this kind of member, or this launch may not message them.
VoshiRateLimitErrorThe daily limit for this member or course was reached.