Skip to content

Talking to the outside world

Most real automations end by telling something else: another system, a person, a spreadsheet. This chapter covers the pieces for that: JSON, dates, web requests and mail.

JSON is the text format most web services speak. mash.json.decode turns JSON text into dictionaries and arrays you already know how to use. Pass true as the second argument to get dictionaries:

set text to '{"order": {"ref": "SO-1", "lines": [{"sku": "A", "qty": 2}, {"sku": "B", "qty": 5}]}}'
set data to mash.json.decode(text, true)
printline data["order"]["ref"]
set qty to 0
each data["order"]["lines"] as line
increment qty by line["qty"]
end
printline "Total quantity: {{qty}}"
SO-1
Total quantity: 7

mash.json.encode goes the other way:

printline mash.json.encode({ref: "SO-2", paid: true, lines: [1, 2]})
{"ref":"SO-2","paid":true,"lines":[1,2]}

Text that isn’t valid JSON is an error: decode records json decode - check your input and gives you nothing. Put an error block after the line when the text comes from somewhere you don’t control:

set data to null
set data to mash.json.decode("this is not json")
error
printline "Bad JSON: {{mash.error.message}}"
set data to {}
end
printline count(data)
Bad JSON: json decode - check your input
0

mash.date.now(), mash.date.today() and mash.date.parse("2026-11-03") give you a date. format writes it out using the letters PHP’s date formats use (Y year, m month, d day, H:i time, D/l day name):

set d to mash.date.parse("2026-11-03 14:05:00")
printline d.format("D j M Y")
printline d.format("H:i")
printline d.isWeekend()
Tue 3 Nov 2026
14:05
false

Adding to a date changes it. After d.addDays(30), d itself is 30 days later. To keep the original, add to a copy with copy().

set start to mash.date.parse("2026-11-03")
set due to start.copy().addDays(30)
printline "Start: {{start.format('Y-m-d')}}"
printline "Due: {{due.format('Y-m-d')}}"
printline "Days between: {{start.diffInDays(due)}}"
Start: 2026-11-03
Due: 2026-12-03
Days between: 30

diffInDays is signed: it’s negative when the date you pass in is earlier than the one you call it on. Pass true as a second argument for the plain distance. Also available: subDays, addMonths, startOfMonth, endOfMonth. Compare dates with < and >. A file’s modified is a date, so file.modified.format("Y-m-d") works.

The script’s clock is the server’s, not your own. If the time of day matters, say which zone you mean in your output (for example format("Y-m-d H:i P") shows the offset).

mash.http makes web requests. get, post, put, patch and delete each take a URL and a dictionary of options:

set response to mash.http.get("https://api.example.com/orders/1182", {
bearer: "my-token",
timeout: 30
})
if response.status is 200
set order to response.json
printline "Order {{order['ref']}} is {{order['status']}}"
else
printline "The service said {{response.status}}"
end

A response has status, body (the text), json (the body decoded) and headers. Useful options:

OptionWhat it does
bearersends an Authorization: Bearer ... header
headersa dictionary of extra headers
querya dictionary added to the URL as ?a=1&b=2
jsona dictionary sent as the JSON body
form_paramsa dictionary sent as a web form
timeoutseconds to wait before giving up
http_errorsset false to get a 404 or 500 back as a response, not an error

Sending data:

set response to mash.http.post("https://api.example.com/orders", {
bearer: "my-token",
json: {ref: "SO-20417", status: "Ordered"},
http_errors: false
})
printline "Created? {{response.status}}"

Never write a password or token into a script that other people can open. Put it in a config parameter, or read it from a service account if your administrator has set one up.

If the call fails (the server is down, the name doesn’t exist), put an error block after it, as in Catching an error in When things go wrong:

set response to mash.http.get("https://invalid.example")
error
printline "Could not reach the service: {{mash.error.message}}"
exit
end

mash.comms.mail.send takes a recipient, a subject and a body:

mash.comms.mail.send(
"accounts@example.com",
"Order SO-20417 filed",
"The paperwork for SO-20417 is now in the customer folder."
)

You can add attachments (an array of files), cc and bcc lists, and a delay in seconds as further arguments:

set report to mash.files.get("Finance", "Reports/week-45.pdf")
mash.comms.mail.send(
"accounts@example.com",
"Weekly report",
"The report is attached.",
[report],
["manager@example.com"]
)

Messages go into a queue and leave shortly after, so a script that sends a hundred emails finishes quickly. Test with your own address first.

The same style works across the rest of mash: CSV and spreadsheet files (mash.csv, mash.xlsx), PDFs (mash.pdf), images, barcodes, SMS and more. The library reference lists all of them.

Decode this text and print the customer’s name: {"customer": {"name": "Acme Ltd"}}. Then build a dictionary with your own name and encode it back to JSON.

Next, you’ll put the pieces together into one useful script.