Respond to a webhook
What it does. Another system calls a web address with ?order=SO-20417. The script finds the documents for that order, reads their Status field, and answers with JSON such as {"order": "SO-20417", "status": "Approved", "documents": 3}. A missing order gets a 404. A call with no order gets a 400.
What you need.
- The MaSH feature.
- A webhook on Settings > MaSH > Webhooks: Add webhook, pick the script, set a Username and Password, Save. The URL column shows the address. See Webhooks.
- Fields on the shares to search: Order No and Status.
- Remember who the script runs as. It searches what its owner can read. See Who a script runs as.
The caller sends the username and password as HTTP Basic Auth, and the address’s key is a secret too. A call without the right credentials is refused before the script starts.
The answer, on its own
Section titled “The answer, on its own”The reply shape is the part to get right. This block turns “what we found” into a response dictionary. A dictionary with a body key is sent as the whole response, with its status, type and headers:
block reply order statuses if order is "" return {"status": 400, "type": "json", "body": {"error": "Give an order number as ?order="}} end
if count(statuses) is 0 return {"status": 404, "type": "json", "body": {"error": "No order {{order}}"}} end
return { "status": 200, "type": "json", "body": {"order": order, "status": statuses[0], "documents": count(statuses)} }end
print mash.json.encode(reply("", [])["body"])printlineprint mash.json.encode(reply("SO-9", [])["status"])printlineprint mash.json.encode(reply("SO-20417", ["Approved", "Approved", "Draft"])["body"])printline{"error":"Give an order number as ?order="}404{"order":"SO-20417","status":"Approved","documents":3}block reply(order, statuses) { if (order == "") { return {"status": 400, "type": "json", "body": {"error": "Give an order number as ?order="}} }
if (count(statuses) == 0) { return {"status": 404, "type": "json", "body": {"error": "No order {{order}}"}} }
return { "status": 200, "type": "json", "body": {"order": order, "status": statuses[0], "documents": count(statuses)} }}
print(mash.json.encode(reply("", [])["body"]))printline()print(mash.json.encode(reply("SO-9", [])["status"]))printline()print(mash.json.encode(reply("SO-20417", ["Approved", "Approved", "Draft"])["body"]))printline(){"error":"Give an order number as ?order="}404{"order":"SO-20417","status":"Approved","documents":3}The script
Section titled “The script”set errorblock to "onError"block onError printline "Error on line {{mash.error.line}}: {{mash.error.message}}"end
block reply order statuses if order is "" return {"status": 400, "type": "json", "body": {"error": "Give an order number as ?order="}} end
if count(statuses) is 0 return {"status": 404, "type": "json", "body": {"error": "No order {{order}}"}} end
return { "status": 200, "type": "json", "body": {"order": order, "status": statuses[0], "documents": count(statuses)} }end
# What the caller sent. A query string or form value is a field on the input.set order to mash.str(mash.event.input["order"] ?? "").trim().toString()
if order is "" return reply("", [])end
set filters to {}set filters["Order No"] to order
set statuses to []each mash.fields.findFiles(filters) as hit set file to mash.files.get(hit["share_id"], hit["path"]) set status to file.fields["Status"] ?? "" if status is not "" statuses[] = status endend
return reply(order, statuses)errorblock = "onError"block onError() { printline("Error on line {{mash.error.line}}: {{mash.error.message}}")}
block reply(order, statuses) { if (order == "") { return {"status": 400, "type": "json", "body": {"error": "Give an order number as ?order="}} }
if (count(statuses) == 0) { return {"status": 404, "type": "json", "body": {"error": "No order {{order}}"}} }
return { "status": 200, "type": "json", "body": {"order": order, "status": statuses[0], "documents": count(statuses)} }}
# What the caller sent. A query string or form value is a field on the input.order = mash.str(mash.event.input["order"] ?? "").trim().toString()
if (order == "") { return reply("", [])}
filters = {}filters["Order No"] = order
statuses = []each (mash.fields.findFiles(filters)) as hit { file = mash.files.get(hit["share_id"], hit["path"]) status = file.fields["Status"] ?? "" if (status != "") { statuses[] = status }}
return reply(order, statuses)How it works
Section titled “How it works”mash.event.input["order"]is the query or form value. Use["name"]with a fallback, so a missing one is empty and not an error.- A dictionary with
bodyis the response.statussets the HTTP code,type: "json"sends the body as JSON, andheadersadds headers. Return anything else (a string, a plain list) and Foldr sends it as the body with status 200. return reply(...)ends the run with that response. A run that ends in an error sends the caller a 500, with the message in the body. Decide your own status codes instead.mash.fields.findFilesreturns one result per matching file, asname,pathandshare_id. It searches what the script’s owner may read.- Output you print goes to the run log, not the caller, once a
returngives a response.
Never put a secret in the answer unless the caller should have it. The address and the password are the only gate. Treat what the script can read as readable by anyone who can call it.
Test it
Section titled “Test it”- Save the webhook and copy its URL.
- From a terminal:
curl -u caller:password "https://<your-address>/hooks/<key>?order=SO-20417". - Open Settings > MaSH > Logs to see the run. Webhook runs also write to the webhook log on the server.
If you can’t get an answer back, check the status code first. 401 means the credentials, 404 means the key, and 500 means the script ended in an error.
Variations
Section titled “Variations”- Take a JSON body. For a
POSTwith JSON, readmash.event.input.foldr_request.contentand decode it withmash.json.decode(text, true). - Check a signature. Many services sign their calls. Read the header with
foldr_request.headers["x-signature"][0], compute the expected value (see the Hash and Crypt pages), and compare. Return 401 when it differs. - Do something, then answer. Write a field or queue a script with
mash.scripts.queue, then return{"status": 202, "type": "json", "body": {"queued": true}}so the caller isn’t kept waiting. See Running one script from another. - Return a page or a file. Return a dictionary with
bodyset to HTML and notype, or return a File to send it as a download. A returned page runs in a browser sandbox. See Webhooks.