Send POST request from a button

Hi,

Wondering if it’s possible to send an arbitrary POST request over HTTP from a button in Coda? I have a few ideas for Microsoft Flow I’d like to implement, but not sure how best to trigger them.

Joe

9 Likes

I don’t think it’s possible to do this directly, but there are certainly a couple of options. The first would be to use a webhook and button with a URL option.

The second would be Zapier or similar services.

1 Like

Thanks Murray. I just wrote a quick microservice with now.sh to forward my ‘gets’ as ‘puts’, and respond with some window closing script and it seems to work, but is a bit ‘hacky’.

Joe

Can you say a bit more about what you did here? It sounds cool. I’m also looking for ways to increase communication between Coda and the outside world, and I saw someone recently had a related question about reacting to row changes (a case that the Zapier integration currently doesn’t cover)

(Hi Nick, I’m not sure about your level of technical knowledge around APIs and Javascript and HTTP calls and stuff so let me know if you’ve been able to follow or whether I need to go into a bit more detail).

Basically I wrote a quick bit of javascript which just receives a ‘GET’ request and makes a ‘POST’ request.

If the ‘GET’ request contains a URL as a query parameter, then it’ll attempt to format the whole query string as JSON and POST it to the ‘url’ it received.

Once it’s 'POST’ed, it’ll either send a piece of JS which will close the window, or write the error to window.

It needs some tweaking because right now, certain specific parameters which I need to pass on to my API endpoint in the URL (eg: API token) are manually pulled out of the query string and added to the URL to post to. In the next iteration, I’ll accept a few different params: url, data, and headers.

Anyway, long story short, right now, if I ‘GET’ https://fake-address.now.sh/?url=api.other-fake-address.com&firstname=Joe&lastname=Innes&apitoken=1234, then the lambda function will ‘POST’ to api.other-fake-address.com/?apitoken=1234 with a JSON payload of

{
  "firstname": "Joe",
  "lastname": "Innes"
}

One easy way to make a ‘GET’ request is to try to open the resource in a browser (which you can do using the ‘Open Hyperlink’ button action).

Now, when you use the ‘Open Hyperlink’ button action, a new window opens up. To avoid this window getting in the way, my lambda function returns either the error, or <script>window.open('', '_self', ''); window.close();</script>, which causes the window to close itself again. It’s a bit ugly, but it works.

Once I’ve implemented the proper way of handling the payload/url separation I’ll make my code public along with deployment instructions so that anyone else who’s interested can spin up their own copy (hopefully either today or tomorrow).

9 Likes

Will be interested to see your process here, I think it might help me with my Integromat integrations :+1:

1 Like

So, the code is as follows:

const axios = require('axios')
const querystring = require('querystring')
const { parse } = require('url')

module.exports = async (req, res) => {
  console.log('Starting... received ' + req)
  const { query } = parse(req.url, true)
  const data = query.data || null
  const headers = query.headers || null
  
  let url = query.url || null

  if (!url) {
    res.end('You must specify a forwarding url')
  }

  let newQuery = query

  delete newQuery.data
  delete newQuery.headers
  delete newQuery.url

  let sep = Object.keys(newQuery).length > 0 ? "&" : "/?"

  url = url + sep + querystring.stringify(newQuery)

  await axios.post(url, data)
  .then(function (response) {
    console.log(response)
  })
  .catch(function (err) {
  	res.end(error)
  })

  res.end(`<script>window.open('','_self').close()</script>`)
  
}

I’m deploying with now (zeit.co/now), which guides you through the account setup, and lets you deploy to the with a single command.

Code also on https://github.com/joeinnes/sortingoffice, along with config file for Now

8 Likes

Thanks for the handy thread @Joe_Innes!

FWIW, I’ve just discovered that you can use a similar trick when catching a webhook with Integromat.

The basic outline is:

  • Create a scenario in Integromat with the Webhook module as the trigger

  • Set up a button in Coda to open the webhook URL generated by Integromat. Use the concatenate() function in Coda to incorporate whatever payload you want to include in the URL. Include any information you might need later in Integromat to look up the tables, rows or columns you want to work with, e.g. I’ve built a very simple example where the webhook passes only the RowId to Coda, so that Integromat is able to look up and modify the row in the later steps of the scenario.

  • In Integromat, add the “Webhook Response” module somewhere in the scenario chain, and use it to return <script>window.close();</script>. That means when you hit the button in Coda, Coda will open a new tab and fire the webhook, but the tab will then close, leaving the user back in Coda.

  • From that point on, Integromat can take care of the rest of the processing, and can use the Coda integration to talk to Coda, e.g. taking data from an external application and using it to update the Coda row containing the button that triggered the webhook.

To give a very simple example, I’ve used this approach to create a button in Coda that creates a OneDrive folder linked to a Coda row (in this case, each row corresponds to a project), and then passes the OneDrive sharing link (restricted to users from the owner’s organisation) back to Coda. The sharing link is then used in another button in that row, which opens the folder for the relevant project. The main benefit of this approach over the “Watch for new rows” integration in Integromat is that the webhook works instantly (no 15 minute wait), which is useful in cases where it’s important not to interrupt the user’s workflow with a wait.

Hopefully I’m not doing anything daft (!), but I’ve certainly found it a useful workaround. Fingers-crossed the Coda team will eventually give us the ability to fire webhooks from a button (or even a server-side function that can be used with Coda automation rules?) without opening a tab.

8 Likes

This is a great example of hacking a solution together when a proper one doesn’t exist. Glad you were able to make this work!

1 Like

FYI all, there’s now an action Xhr() which is experimental and undocumented, but it’s fairly clear what it does if you check it, so my hack above shouldn’t be needed any more.

1 Like

Yes, but it doesn’t work yet. Xhr() sends all requests through proxying Coda endpoint… one problem though, the endpoint doesn’t exist yet, and all requests fail.

Thanks @Joe_Innes for your clever hack.
Looking forward to seeing the XHR function but in the meantime this will do

Hi @Tim_Sherman ,

I’m currently building something similar to what you did, but I don’t really understand how you used concatenate to include a payload in the webhook url. If you could describe that process this would be VERY helpful. Thanks a lot,

Jay

Hi Jay

Sure thing. In my example above, I want to be able to create a new “Project” record in Coda, and automatically have Integromat create a OneDrive folder specifically for that Project, with the OneDrive folder URL passed back to Coda so that the project folder can be opened via a button in Coda.

In Coda, I have a table in which each Project is represented by a row. So each Project has a RowID, and other columns such as Project Name, Owner, etc. I also have a button column in the Projects table, with the button action set to “Open hyperlink”. If there isn’t already a OneDrive folder URL recorded in the “Folder URL” column for a Project, I want the button to fire a webhook to Integromat to create a new OneDrive folder. If there is already a OneDrive folder URL for this Project, I want the button to open the folder.

To achieve this dynamic response from the button, I set the button’s link URL to:

if(thisRow.[Folder URL].IsBlank(), Concatenate("https://hook.integromat.com/xxxxxxxxxxxxxxxxxxxx?RowID=", thisRow.[Row ID]), thisRow.[Folder URL])

In my real doc the “xxxxxxxxxxxxxxxxxxxx” is actually the long string that uniquely identifies the Integromat webhook (you can get this from your Integromat webhook module).

Now, getting to your question, the payload here is simply the Coda RowID, which I want to pass to Integromat via the webhook. I use the concatenate function to generate the webhook URL, which includes the RowID for the relevant Project.

On receiving the RowID via the webhook, Integromat uses it to retrieve the Project Name from Coda, and creates a OneDrive folder named with the RowID and the Project Name, e.g. “1. My First Project”. Integromat then passes the new folder URL back to Coda to populate the “Folder URL” column for the Project, again using the RowID to ensure the URL is logged against the right Project in the Projects table in Coda.

In case you are wondering, I also use an if statement to label the button in Coda, so that if the “Folder URL” column is blank, the button label is “Create OneDrive Folder”, and if the column is populated, the button label is “Open OneDrive Folder”.

Hope that helps!

2 Likes

Okay I’m late on this but this is amazing! Will definitely use this trick. Thanks a lot for your time @Tim_Sherman

1 Like

i try but not working

1 Like

Hi all, I’ve used some of @Joe_Innes’ insights to build a doc that not only sends a request but also brings the response data back into Coda:

have video tutorial?

I’m getting an “Unable to Execute Invalid Action” error
Is this method still working for you?

Hi there. Yes, still working for me. Was it working for you before? Let me know if I can help trouble-shoot.