Call a web API and store the answer in fields
What it does. A document arrives with a Company Number field. The script asks an outside web service about that company, then writes the company’s name, status and address back into the document’s fields. If the service is down or doesn’t know the company, the script says so and leaves the fields alone.
What you need.
- The MaSH feature.
- Fields on the share: Company Number (read), and Company Name, Company Status and Company Address (written).
- The trigger Captur Extracted if Captur reads the number, or Fields Changed if a person types it. See Events.
- Script settings:
API_URL(for examplehttps://api.example.com/companies) andAPI_TOKEN. Keep the token in a setting, never in the script text. - Outbound access. Foldr’s own internal addresses are always blocked. On an appliance an administrator can allow other hosts. See Foldr Cloud and appliances.
Mapping the answer, on its own
Section titled “Mapping the answer, on its own”The part that changes with every service is how its JSON maps to your fields. This block does that mapping. Test it with a saved sample answer, before you ever call the service:
block mapCompany answer if not answer return null end
set address to answer["address"] ?? {} set parts to [] each ["line1", "town", "postcode"] as key set piece to mash.str(address[key] ?? "").trim().toString() if piece is not "" parts[] = piece end end
return { "Company Name": answer["name"] ?? "", "Company Status": mash.str(answer["status"] ?? "unknown").lower().toString(), "Company Address": mash.collect(parts).join(", ") }end
set sample to mash.json.decode('{"name": "Acme Ltd", "status": "ACTIVE", "address": {"line1": "1 High St", "town": "Leeds", "postcode": "LS1 1AA"}}', true)
set mapped to mapCompany(sample)each mapped as field, value printline "{{field}} = {{value}}"endCompany Name = Acme LtdCompany Status = activeCompany Address = 1 High St, Leeds, LS1 1AAblock mapCompany(answer) { if (not answer) { return null }
address = answer["address"] ?? {} parts = [] each (["line1", "town", "postcode"]) as key { piece = mash.str(address[key] ?? "").trim().toString() if (piece != "") { parts[] = piece } }
return { "Company Name": answer["name"] ?? "", "Company Status": mash.str(answer["status"] ?? "unknown").lower().toString(), "Company Address": mash.collect(parts).join(", ") }}
sample = mash.json.decode('{"name": "Acme Ltd", "status": "ACTIVE", "address": {"line1": "1 High St", "town": "Leeds", "postcode": "LS1 1AA"}}', true)
mapped = mapCompany(sample)each (mapped) as field, value { printline("{{field}} = {{value}}")}Company Name = Acme LtdCompany Status = activeCompany Address = 1 High St, Leeds, LS1 1AAThe script
Section titled “The script”set errorblock to "onError"block onError printline "Error on line {{mash.error.line}}: {{mash.error.message}}"end
block mapCompany answer if not answer return null end
set address to answer["address"] ?? {} set parts to [] each ["line1", "town", "postcode"] as key set piece to mash.str(address[key] ?? "").trim().toString() if piece is not "" parts[] = piece end end
return { "Company Name": answer["name"] ?? "", "Company Status": mash.str(answer["status"] ?? "unknown").lower().toString(), "Company Address": mash.collect(parts).join(", ") }end
set apiUrl to mash.config.get("API_URL")set token to mash.config.get("API_TOKEN")if not apiUrl or not token fail "API_URL and API_TOKEN must be set on this automation."end
set doc to mash.event.fileif not doc returnendif doc.is_dir returnend
set number to mash.str(doc.fields["Company Number"] ?? "").trim().toString()if number is "" returnend
# Already looked up? Then stop. This also stops the script triggering itself# when it writes the fields below.if mash.str(doc.fields["Company Name"] ?? "").trim().toString() is not "" returnend
set response to mash.http.get("{{apiUrl}}/{{number}}", { "bearer": token, "timeout": 20, "http_errors": false})
if response.status is 404 printline "No company {{number}} at the service." returnend
if response.status is not 200 fail "The service answered {{response.status}} for {{number}}."end
set mapped to mapCompany(response.json)if not mapped fail "The service sent an answer I could not read for {{number}}."end
each mapped as field, value if doc.fields.has(field) doc.fields.set(field, value) endend
set companyName to mapped["Company Name"]printline "Looked up {{number}}: {{companyName}}"errorblock = "onError"block onError() { printline("Error on line {{mash.error.line}}: {{mash.error.message}}")}
block mapCompany(answer) { if (not answer) { return null }
address = answer["address"] ?? {} parts = [] each (["line1", "town", "postcode"]) as key { piece = mash.str(address[key] ?? "").trim().toString() if (piece != "") { parts[] = piece } }
return { "Company Name": answer["name"] ?? "", "Company Status": mash.str(answer["status"] ?? "unknown").lower().toString(), "Company Address": mash.collect(parts).join(", ") }}
apiUrl = mash.config.get("API_URL")token = mash.config.get("API_TOKEN")if (not apiUrl || not token) { fail "API_URL and API_TOKEN must be set on this automation."}
doc = mash.event.fileif (not doc) { return}if (doc.is_dir) { return}
number = mash.str(doc.fields["Company Number"] ?? "").trim().toString()if (number == "") { return}
# Already looked up? Then stop. This also stops the script triggering itself# when it writes the fields below.if (mash.str(doc.fields["Company Name"] ?? "").trim().toString() != "") { return}
response = mash.http.get("{{apiUrl}}/{{number}}", { "bearer": token, "timeout": 20, "http_errors": false})
if (response.status == 404) { printline("No company {{number}} at the service.") return}
if (response.status != 200) { fail "The service answered {{response.status}} for {{number}}."}
mapped = mapCompany(response.json)if (not mapped) { fail "The service sent an answer I could not read for {{number}}."}
each (mapped) as field, value { if (doc.fields.has(field)) { doc.fields.set(field, value) }}
companyName = mapped["Company Name"]printline("Looked up {{number}}: {{companyName}}")How it works
Section titled “How it works”- The settings check fails the run with a clear message if the URL or token is missing, so a half-configured automation says why it did nothing.
"http_errors": falsehands you a 404 or 500 as a response you can look at. Without it, a bad status is an error and the script has to catch it. See the outside world chapter.timeout: 20stops a slow service eating the whole 60 seconds of a queued run. See Limits.- Three outcomes are handled on purpose: not found (a quiet line), any other status (
fail, so the run shows Failed in the run log) and an answer you can’t read. - The guard on
Company Nameis what stops a loop. Writing fields can fire Fields Changed for the file when the run ends. The next run finds the name filled in and stops. doc.fields.has(field)skips a field the share doesn’t carry, instead of failing. Remove that check if you’d rather know.mapCompanyis the only part you rewrite for a different service.
Variations
Section titled “Variations”- POST instead of GET.
mash.http.post(url, {"bearer": token, "json": {"number": number}}). The tutorial lists the options. - Retry later. On a 5xx answer,
failand let a nightly schedule pick up documents whose Company Name is still empty. - Refresh monthly. Add a Company Checked date field. Skip the lookup when it’s under 30 days old.
- Send data out, not just in. Post the document’s fields to an order system the same way. See Respond to a webhook for the other direction.