afternoon everybody and welcome along to Explore Curl for FileMaker. My name is Steve Winter, I have the unfortunate, I don't know, circumstance of being one of those people that the border officials of the US don't really like. I was born in New Zealand, I travel on a British passport, I live in Latvia and I've been to Iran. You put all those things together and red flags wave everywhere. I've been a FileMaker developer for a lot longer than I've had gray hair and most of my gray hair probably comes from being a FileMaker developer. I'm a freelancer, I run my own business, most of what I do is in the custom web publishing sort of space, but as well as that I do a lot of work integrating FileMaker with other cool stuff. I've been a fairly regular DivCon speaker, last year I wasn't here, last year I decided I was going to take myself off on a little adventure and I was going to travel from Beijing and China to Istanbul and Turkey and if that wasn't mad enough I decided I was going to do it on a bicycle. Five months and eight and a half thousand miles later I'm still alive to tell a story. One of those things you're really glad to have done and if you ever do it again it's too soon. So today what we're really going to look at despite all these bullet points is we're just going to look at one single script step that got a major overhaul in FileMaker 16 and that is our good friend insert from URL. Here's the insert from URL config from FileMaker 15 and pre. You can see that it's got four options available to it. If you haven't tried the script step in FileMaker 16 it doesn't look a lot different. It's just got this one really cool little bit down the bottom here which lets us specify curl options and that's really what this session is all about. It's about that change to the FileMaker insert from URL script step. So what is curl according to Wikipedia it's this. What that really amounts to is it's a way in which systems can interact with remote web locations. In FileMaker it's been under the hood since FileMaker 12 when the insert from URL script step first arrived. In FileMaker 12 you could use HTTP, FTP and you could do a get kind of like loading a page in a web browser. In FileMaker 13 it got extended to add the ability to do something that was called HTTP post or HTTPS post which was just kind of FileMaker's way of squeezing another protocol the post protocol into what was already in place. But we were very very limited to simple situations where any information could really only be passed in the query string. In FileMaker 16 we got access to those curl options. There are lots of them. There are loads and loads and loads of them. That's the link to the manual page for the curl which is the underlying library that's used in here. FileMaker offers most access to most of those options. There are few I've noted there which are mostly around some pretty obscure security that you're probably not going to end up wanting to know about. There are a couple of differences between curl from the command line or curl in other programming languages and FileMaker and one of the most notable ones is that you don't have access to the file system. FileMaker have provided a way that we can work around that and you'll see that working shortly but it is something to be aware of. If you're used to working with curl in other places you can't use file system access in FileMaker. The other one is that at present we've still only got HTTP and FTP protocols. FileMaker please can we have a few more? Curl itself supports IMAP and SNTP and POP3 and more protocols than we've all combined heard of. Unfortunately we're still with HTTP and FTP at present. I'm going to introduce you to a number of different options. These are the ones that end up being the most useful immediately to us. Data lets us send data and as well as being able to send string data we can also send binary data. The one that's made the most difference in terms of being able to immediately access APIs we couldn't before is the second or the third one there, the header option because that allows us to send header information with our request and that unlocks a huge number of APIs that previously we would have had to use a plugin or something of that sort to be able to access. For those people who are working in a corporate type environment being able to access the proxy header can also be very useful because it may allow you to get out from your network when previously you may not have been able to. So let's see this work. Here's my FileMaker solution. In my business I have an outsource supplier that provides me with leads so that I can generate my business. I've got my FileMaker contacts database you might recognize this one. It seems to get used pretty much for every demo at DevCon. But what I want to do is I want to get some contacts from my lead source supplier. So up here I've got my load new leads button. I've clicked on that and you can see that immediately my record count went from 9 to 18 because I've pulled in 9 new leads from my external API provider. I do it again and because of the way that API works I get 9 more. So what's happening? Let's have a look at the script that's underneath that button. First of all what it's doing is it's logging in to an API. You can see that I'm setting a variable here in the middle that lets me set the URL and that's really just a very simple URL. In order to access this API I have to send some data parameters to them. The way this API works is that it wants a JSON object with a username and a password. Here's another one of those brand new FileMaker 16 functions happening for us. JSON set element. If you're not familiar with this what it allows us to do is it allows us to create a JSON object with a single script step. Hurrah! And then we can add to it. The first time I'm calling it I'm passing in an empty object. I'm saying make me some JSON starting with nothing. In there put a user element which has the value intro username and then the digit one at the end tells the set element that I'm passing it a string. That this is a text string. So I'm building my JSON that I need for my API into a variable. And then this is the bit that's really interesting. This is the FileMaker 16 bit. This is me putting together some curl options that I'm going to send with that request. The first line says hyphen D, I want to send data. And you'll see that I've put ampersand dollar data. And so what that is, the ampersand says hey FileMaker, I want you to go to this variable dollar data, pull out what's in there and send it as part of this request. If I didn't have the at there it would be translated as a literal string dollar data that I ended up with. So the at is telling FileMaker when the curl options are passed to take the content of the variable out. The second one is the header option and that's where we have the ability to send a content type. The API at the far end of this example expects to get JSON and it expects to be told that it's going to get JSON. And so I'm able to set that header and say hey API, here comes some JSON. Then the last thing I'm doing is another really, really useful curl option. I'm setting something called a cookie jar. Many of you would be familiar with cookies from websites where when you log in a cookie gets set with some authentication data. And the same thing is happening with this API. This API is setting a cookie data so that each time I go back to use that API again, I don't have to log back in again. I use that cookie to identify myself. And so this cookie jar curl option allows me to tell FileMaker hey, if you get any cookies back from this request, stick them in this global variable for me. And so what happens here is that my cookie jar variable gets populated with the content that comes back. Because I have already made that request, if we go over to the data viewer, we find that I've got a cookie jar variable. And in there, you can see that what I've got back is the cookie content. So the API.msdev.co.uk server has set a variable which is called PHP session ID, and it has that string in it. So that's identifying this particular login for future requests. Let's have a look at where that gets used. When I go to get my records, what I do is I set an option, a series of options here. Again, I tell it that I want to send JSON to it. But I also say I want to send a cookie back. And this time I'm sending the variable cookie string. Now the observant among you will notice that that's not the same variable. And there's a really important reason for that. What we get back in our cookie file is a return-separated series of information. But when we send that back out again, we have to send just a string. If you find yourself working with APIs, which use cookies, this is a really, really important tip. And it's one that will make you scream for a really long time till you know it. Let me show you. In here, where I'm sending my cookie string, it's really simple. All I have to do is I have to replace FileMaker's hard return symbol with character 10, which is the new line string, the new line ASCII code. And simply doing that means that I convert that return-separated list of stuff that came from the server into a string that can be sent back again. If you don't know that, it won't work and you'll hate it. So that's what's happening. The rest of what happens in the script isn't really very interesting. I'm basically getting the response back and then I'm splitting that up into rows using my JSON getElement. And then I'm just looping through all of the rows that came back from the API. And I'm creating fields, setting records, creating records, setting fields. One other thing that is worth mentioning while we're looking at it here, JSON get element, unlike a lot of things in FileMaker, is case sensitive. So if you take a look at that, you'll see that the last one where ContextWorkPostalCode gets set, it gets set to get JSON element zip all in caps. Because that's what the API is returning. It's inconsistent with all the other things that are coming back from that API. But it is important. If you set it to anything other than what it is in the JSON, you will, in terms of case, it just won't work. Oops, wrong way. So we looked at cookie jar, which is what you use to receive cookie information. We looked at cookie, which is what you use to send it back. The other thing that we didn't look at in that particular example, but you'll see quite a bit later on, is this hyphen x or hyphen hyphen request. I said in FileMaker 12, we were limited to the get protocol. 13 gave us post, but as well as that, there are a whole bunch of other verbs that you can use with APIs. You can use things like put, or patch, or delete. And this hyphen x option, and that's a capital X, not to be confused with the lowercase x. It does something different, allows us to tell the call to the API exactly which protocol it is that we want to use. Just reiterating what I said about cookie jar because I had so much frustration with this. So there's a blog post all about it. It shares some of my frustration. But basically, you need to make sure that you convert it back into a string. The other thing worth mentioning is about spaces. The way in which options are concatenated together is you have hyphen hyphen option, space, the parameter you're passing for the option, space. Now, this all looks like it might and that it should work. But if I try to use this particular set of options with a script, it will not work. And the reason it will not work is because of that. See just there between that colon and the A, there's a space. So what ends up happening is that the header stops after the colon. And curl gets something called application slash JSON. And it's got no idea what to do with it. So when you're putting in options, particularly for things like headers, which can often contain quite long strings, it's important that you make sure you escape that somehow. It doesn't really matter how you do it. You can put it in a string and then use FileMaker's quote function. You can use the bark slash before the quote as I'm doing there to say, this bit is a quote. It's a single string. So something just to be a bit cautious of is if you inadvertently end up with spaces in those options, then things will go horribly, horribly wrong. You can see the way that I'm putting these options together is simply to put each on a line on its own, end with a space and put an ampersand after it. And I find that to be quite an easy way of making it really clear what you're doing as you're setting your options. So I said that we'd look at this idea of put and delete and those other functions that are available. So I thought I'd use a really simple sync solution as an example for that. This is probably a bad idea, because sync is hard. Those guys here who've written sync solutions know sync is insanely hard. But I'm gonna make a really simple use case and we're gonna do it anyway. So basically, this gives us a good example to demonstrate relatively easily those other verbs that we can use. And it lets us play with the data API a little bit, which another cool shiny thing we might as well. So what I'm gonna do is I've got a hosted app on Farmacre Server that has the data API available. I've got a remote app on my phone with very limited connectivity. And what I wanna do is I just wanna push data from my phone up to my remote server and then be able to update or delete the records from there. The Farmacre Data API, it's new in 16 as well. There was a session this morning from the guys at Goyer about it. So go and talk to them if you wanna know more about it. And then James is doing a session tomorrow all about it. So that's all I'm gonna say about that. Go and see James' session tomorrow at 10.30. In summary, you have a series of URLs that you pass data through. And you call those using those different rest verbs, those different HTTP verbs. And there's basically a mapping between them. Create you post to read you get, to update you put, to delete you, delete. And so those are the different ones that are available and that are useful against the Farmacre Data API. Other things like patch or myriad others that come into play with other APIs. So let's have a quick look at that working. So here is my file which is on my server. Here is my phone which has my remote app on it. I'm going to create a new record in here. Let's have Ms. Conference. So I've created my new record and I tap up here at the top and it goes off and it all goes horribly wrong. And that's what we love when we're doing demonstrations live with APIs. Let's just have one more go at that because it worked about three minutes before my session started. But I bet I get the same result because why wouldn't I? It's thinking, it's thinking, it's thinking. Okay, that's great. Oops, go away, go away. Let's come back to my demo file and let's at least have a look from the perspective of my demo file of what it's trying to do and we'll see if we can see what's going wrong. Basically what's happening is that I'm running this script to sync a record. It's checking to see if the record exists or not based on a field. If it doesn't, it's gonna do a create contact. We'll just quickly set a whole bunch of parameters here. So we're gonna log into our remote API using the FunMaker API.  And I am setting my curl variables, which I can't see in script debugger. I'm setting my curl options to send that data that we need. And we run the insert from URL and it kind of looks like it all worked over here. Isn't that convenient? Damn! We'll zoom back out and make that go away and we should find that we've got a new contact in here called Willard. Let's change Willard to Fred. And my little indicator up here tells me that there's been a local change. I click and it all runs and Fred updates over here. Why didn't it work on my phone? It's the same file! Turn on Wi-Fi, that's. Turn off Wi-Fi. Let's not bother, the idea is the same. Basically it's the same database calling the same scripts. Probably turn off Wi-Fi, turn Wi-Fi on would probably solve it, but it's not worth going into. Essentially if we look at the scripts that are going on there, perhaps in the right file, we are logging in and as we are logging in, what we need to do is we need to pass in the data and we need to set the header type in here. When it comes time to actually create or update that record, we need to send back the token that we get from logging in. So if we take a look here, you'll see that we're doing a bunch of extra things in here. This is when we're updating, so we're doing that put. So you can see I've got hyphen X put and that's the way in which we say, this record already exists, I want you to update it with the new data that I'm gonna send. We send a custom header called fmi-data-token, which is the token we get back from logging in.  You can see this time I'm using hyphen-data-asci, that just means that this is gonna be a text string that you get. It doesn't really matter that I'm using that. In this case I could have just used hyphen D because I'm sending text. OAuth from FileMaker. So the thing about OAuth is that a whole bunch of the world seems to like using OAuth. And the reason for that is that it really simplifies the management of credentials and users. And so one of the things that we want to be able to do with a lot of FileMaker-related apps is that we probably wanna be able to access these OAuth-based APIs as well. As I've said, the short answer is really because Google. Everyone wants to be able to stick stuff in Google Calendar or update Google Drive or any of those sorts of things. And OAuth is what Google uses for their security, as does much of the rest of the world. OAuth is basically, as it says here, a way of managing and authenticating people without the service that wishes to use those people that uses credentials and having to know what they are. So I have a client who use FileMaker to run their whole inventory of classes that they run. They have a shared calendar and Google into which they wanted all of those classes to go. And so we wanted to be able to get the data out of FileMaker and into Google Calendar. Basically the square on to the left shows the FileMaker view of the different courses and over on the right we've got the different sessions that are being created in that Google Calendar. In order to access Google's APIs, you have to have something that's, you have to have the credentials and you have to have one of two things. You either have to have a way in which you can create a service account, an account that can manage a calendar, an address book, any of those things on behalf of another user. If you're working in an enterprise, any kind of environment that use Google for a lot of things, then probably going down this route of a service account is the way to go. So from within the Google console, you create a project. From there you create the service account and you receive a bunch of credentials, a JSON array of credentials that you get from Google. You can optionally give that service account domain wide access. And this is where I say, if you're working with a large organization, this can be really useful because it means that that service account can put calendar entries into that shared calendar, but it can also put calendar entries into the accounts of individual people. If I go back to my picture here, you can see that we're pushing data in here and this is going into the shared calendar, but this account here, you can see where the squidgy bit is. That's the name of the person who is teaching that class. And so as well as pushing that class into the global shared calendar, that class also gets pushed into that teacher's account at the same time. And that's able to be done because we're using the service account for the domain that all of these users belong to. So our shared calendar gets everything and the individual teachers get just the things that they're attention. And they're all pushed with the same set of credentials through that Google service account. You get back, when you register your application for this, you get back a credentials file. It has a whole raft of stuff in it, a lot of which we don't need to worry too much about. The first one is this client email, which is an auto-generated email-like thing. That's the login that's gonna be used for this service account. We get a private key. Requests to this API have to be signed and so we get that private key that we use to sign those requests. And then the last thing we do is we get told where we, how we go about authenticating, the endpoint to authenticate against. To actually connect to these types of services, we have to these Google services anyway, certainly through OWLF, we have to create this thing called a JSON web token, a JOT. We use that to request an access token, we receive that token, and then we use the token to make our subsequent requests. With me so far? Okay. That JOT has three key parts to it. It has a header in which you specify some metadata about the request you're about to make. It has something called a claim set where you say, this is what I wanna do when you let me log in. And then it has a signature, which you have to generate using that key that you got previously.  Now, the thing about that signature is that we got some new encoding functions and FileMaker 16, but unfortunately they won't do signing of things. They'll shard 256 hash things, but they won't sign them. So we have to come up with another way to deal with signing those requests. Let's have a look at how we can make that work. Here's my script that's gonna get us signed and authenticated against Google. First of all, I have to build that JOT. So I start off by putting the bits of it together. First of all, I need the JSON that I'm gonna send. I'm gonna sign this with RS256, which means that it is shard 256 signed with an RSA key. This stuff is always the same. You just read the documentation to find that out about because there's only one option and this is it. Easy. What we then have to do is we have to base 64 and code it. But we have to base 64 and code it in a really, really special way. We have to base 64 and code it according to RFC4648. Who cares what it is, but you just have to do it. Just trust me on this one. And the other thing is having done that, you then have to actually strip out some of the characters that end up in there because otherwise Google freaks out when you get them at the end. This is another one of those things that you only learn after doing a lot of swear. I mean, a lot of creative coding. Or in fact, what I did to work this out was I looked at the PHP API that Google provides and they do something very similar to this. And so by looking around the ecosystem around the Google, I was able to find out how to go about getting this to work in FileMaker. This is an ideal location for a custom function.  Just make that go away. So we basically do the same thing with the claim. Oops, not that one. In the claim, we have to put together a whole bunch of other stuff in JSON. We have to say when it was issued, we have to say what the scope is, which means what do we want to do with that token once we've got it? We have to say where are we going to log in, which is the audience. We have to say when it expires, which is one hour from now. And now has to be the number of seconds since the midnight on the 1st of January, 1970. So you'll see in the script, there's something there that's set to now so that we can work out when that is. And then we say issued at, that we issued it at this point in time. We do the same thing with that. We base 64RFC4648 and code that. Now we have to create the signature. And basically what we do with the signature is we take the two bits we've already got, we join them together, and then we sign it. Now I said we can't do that in FileMaker, so I thought since I'm doing an session about JSON, sorry, about curl and about APIs, I'll write an API to sign things for me. You know, why not? The end result is that it actually works really, really well. So what I'm doing is that I'm pulling out that private key that I need from the Google data, and then I'm posting that off to a URL with the data that I want that's gonna sign it. If you wanna do this, I don't know whether Steven Blackwell would agree that it's safe to do because you're sending your private key over a network connection to the signing point. But if you're running that API on your own server, in your own infrastructure, possibly even on the same computer, then I leave it to yourself to determine. But if you can come up with another way to get an RSA 256 signed key in FileMaker, please let me know. If you wanna try running this yourself in the community, the PHP code that sits at the end of that API is there and you can have a go at it yourself. Basically, what we end up doing then is we end up getting these three bits that we need. We get the header, we get the claim, and then we get the signature that we've just got back from our signing API. We concatenate all of them together with three periods, and then we post all of that off. That's all we do. We just say that we're explicitly posting this, and then we send the data. You'll see that I'm doing something slightly different to escape the data in this situation. I'm wrapping single quotes around the data string, okay? And that's just another one of those precautionary measures. Just in case a space ends up in there. All of that substitution that we've done should have taken care of that. But if we ended up with a space in our string just because and it was sent, it wouldn't work. We fire that off to Google, and according to the history of life, the universe, and everything, we get a token back. So let's try running that script. And let's take a look in our data viewer and all going well, we've now got something in here called Google Token. So basically what we've done is we've taken all of that information, we've fired it off to Google, and they've come back and said, hey, we know who you are, you're allowed in, here's a token that you can use to make subsequent requests. Now having done that, we've actually solved the whole problem. From this point forward, actually putting calendar entries in someone's calendar is trivial, because all we have to do is use that token, send a bunch of data, and the entry will be created. We take a look at this script, where setting a URL, where setting some options in here. So we send a header that's called an authorization and it has a bearer, and that bearer is the token that we've just got back from the authenticate session.  And you see I've shown you a different way of making sure that the data that you send gets quoted correctly. I've wrapped my data variable in the quote function. So there are a whole myriad of different ways that you can deal with the fact that you've gotta make sure you've only got continuous strings in any data that you want to send. If we zoom back out there and we run that script, we should end up with a response. So here's our response that's come back from Google. It's telling us that we've created a calendar event. It's confirmed, it gives us a URL, we can access it. And you'll see that what we've done is we've created a calendar entry for where we are right now. One of the things that I didn't mention in the data that we're sending, you'll see there that the time start and end are a time string with a T in the middle and a Z on the end. And that's basically saying that this is in UTC. In my script, when I'm setting that, you'll see there that the start date time and the end date time that I'm setting are the same thing. So I'm setting this in UTC and that's the simplest way to deal with interacting with the Google API is to use UTC time because that way you don't have to deal with sending the time zone data as well. If you happen to be like me and you happen to have clients who are in UTC, it's also a whole lot easier. But for you working in the US, you can do one of two things. You can do as I've done and say that 2.15 this afternoon is 21.15 UTC and send the UTC value. Or you can also add to this particular date time format. You can add time zone data after the last hour, minute, the second bit at the end. So after the six colon zero zero, in place of the zero zero, that's where you put the time zone data. I don't remember what it is for your time zone. Just look it up. It's called an atom time format that you need to use. Okay, and just to complete the circle, if we find what I've done with Firefox and we go to Google Calendar, you can see that there is that entry and I must have got my UTC time zone right because the red line is right in the middle of it. So that's basically what's happening. That last step is just pushing that data across to Google and the calendar is being updated. I am just gonna fiddle with my phone briefly because I really do want to do this next demo from my phone as well. So I'm just gonna turn the wifi off. That seems to be working. I just got a wifi message, so that's good. Let's go back to there. Okay, the last demo that I'm gonna do is about exchanging data with Dropbox. I hope by now you've kind of got the sense that this is all very much the same. Every time you want to interact with a different API, what you need to do is more or less the same. You need to look at the documentation to find out what data you need to send. You need to find out how to authenticate and then you need to put all those pieces together with the right options in your curl request. If you want to exchange data with Dropbox, you need to go through a couple of steps along the way. In your account, you have to create an app, then you have to get an access token, and then when you want to make requests, you have to use that access token in a header. Same sort of thing as we've been doing with all of the other examples that I've shown you. Phone, you will behave. Dropbox is here. Okay, so if I come down here at the bottom of my phone, the wrong layout, I think I just worked out why the last demo didn't work. I've got the wrong file open on my phone. I've got the old one that doesn't have the new stuff. Okay, let's not do that from there. Let's go to this one over here. I've got a container here. I'm going to very quickly make a screenshot. No, I'll just click in here. Let's do this the other way around. Let's look at getting stuff from Dropbox. If we take a look at here, we can retrieve a file from Dropbox. Let's have a look at doing that.  If we look at my Dropbox account that I'm using for this demonstration, you'll see that there are a couple of files in there. There's a photo of me from when I was preparing this, and there's the Getting Started with Dropbox PDF. What I want to do is I want to pull that file into my Dropbox container. So here's my container, here's my script. I'm going to retrieve my file from Dropbox, and you can see that I end up with that PDF in there in one click, which is kind of cool, I thought, but apparently you don't, nevermind. Okay, so, especially since I'm having trouble, all right, come on. So basically, what we're doing here is all much the same as we're doing everywhere else. We are doing our login, and then we are listing the files that are in our Dropbox container. So usual stuff, we are saying that we want to look at this endpoint and the API called listFolders, and we are passing in some data, saying that we just want the path of nothing, the root path of our Dropbox, and that we need a bunch of options to make this work. We have to do a post, we have to send some data, we have to tell it we want JSON, and we have to send it that token, that token we got when we created our Dropbox app. So we use that token in all of our requests to Dropbox, and that allows us to talk happily to their APIs. The token that you get from Dropbox does not expire, no, and that's actually a really good point. The tokens like the one that you get issued from Dropbox last forever or until they are canceled through your Dropbox account. The token that we were just getting from the Google API does expire. It expires after 3600 seconds, which is one hour. And so if you try and use a token after one hour, it will, you'll get an invalid response. You'll get a 403 header back saying that access is denied. So any scripting that you're doing around the Google API, you have to check for that use case and then just re-authenticate again if necessary. That there are a couple of more sophisticated ways that you can do that. When your tokens come back, rather than just getting one token, sometimes you get two. So you get a reissue token that you can use to get a new access token without having to go through the login process again. But yeah, it is something to be conscious of that tokens do often expire. The Dropbox one's Dropbox one doesn't. So when you're playing with the demo file, then you will be able to upload stuff to that Dropbox account. Please remember it's a family show. Okay, so we are setting our options and then we are getting a list of the files that are in our Dropbox back. When we want to retrieve that file, we have to look into that file list at the JSON that comes back. We told Dropbox, hey, we want JSON. And so surprisingly enough, Dropbox gave us JSON back. And what we get back is this great big array that tells us all about the files that are in there. Now you can see here that what I'm doing is I'm finding out the name of the first file, although actually I'm not getting the name of the first file. And this is another one of those, that's a little bit odd moments. You'll see here that I've said that I want entries, square bracket one. Now as you know, when you do getValue in FileMaker, getValue variable one will get you the first one. In JSON, it's zero indexed. So this one will trip you up if you're not aware of it. Basically arrays in JSON in FileMaker are zero based. Obviously they've done that to be consistent with the way that JSON works in the rest of the programming world. Unfortunately, it means it's counterintuitive for those of us who are used to FileMaker with the first one being an index one, not index zero. So this is actually not gonna get me the first file path. It's gonna get me the second one because it's gonna get me the one that's in position one. And then what I do is I say entries as an array, give me the first one, and what I get back is an object. And so then I put that dot path underscore lower to say from that object, give me the parameter or called path underscore lower, give me its value. And so that's basically getting me that value. Then what I'm able to do is come down here and use that to create some very, very interesting looking JSON. This is another one of those what moments? I have to send this JSON in a header. And so when I send the JSON in the header, it has to have a backslash before the quote to escape the quote, to stop the quote screwing things up. But then weirdly, the backslash character is a protected character. So I have to put a slash in front of the backslash character to escape the backslash character, but in order to escape that, I have to put a slash in front of it. Trust me, it works, but it's crazy, but it works. And now you know this too, so you don't have to have a lot of creative language whilst you're coding, working out how to go about escaping JSON so you can stick it in a header. Why Dropbox want JSON in a header in an API is just, like, don't even go there. It's like, thank you, it's gonna be very secure. So basically what you can see here is that that one second header, I'm setting Dropbox-API-ARG, I'm setting that to that data variable that I've just built, which has got my mega escaped JSON in it. And then the last thing I'm doing is I'm saying that I want to get that data that comes back, because what I'm requesting here is a file after all. So I have to do something with the stuff that I get back. And that's where there's another one of these really super useful curl options called output. And what I do here is I specify that the output, what that thing will be called, is the variable I got before, which was that first file name. So I'm saying when I get this stuff, what I want to do is I want to put it into this output with the name of first file name. So what I'm doing with all of this data is that I'm doing an insert from URL and I'm inserting that output that comes from that curl request. I'm inserting that into a container, okay? Did simple. I'm getting a binary stream of data from Dropbox and I'm bunging it in a container. Now the interesting thing about that is that FileMaker is really happy to receive that binary data string and to stick it in the container. And it works, it works really well. The thing that doesn't happen is that FileMaker's ability to display PDFs or to display images doesn't kick in. And it doesn't because it's not the right type of file. FileMaker doesn't know what type of file's gone into the container. So to get around that, we do a little bit of a shuffle. What we do down here, excuse the seasickness bit, but what we do is we look at the extension of the file. We work out what type it is. If the type of the file is an image, then we, sorry, line 37, we've exported the field contents out to a temporary path. We then work out what type of file it is and then we call an appropriate insert function based on the type of the file. So if I got an image back, then we just use insert picture. If we got a PDF back, we just insert from PDF. And if it's neither of those things, then it just stays as a binary file. So if it were a Word document, which FileMaker can't deal with at all, anyway, then it'll just still be a Word document in the container. But by doing this little shuffle where you get it in and spit it out and pull it in again, it means that that stuff like images and PDF rendering can work really, really nicely in your solution. I will just very briefly show you the file that's used to send container contents, even though it didn't wanna work for me. But basically I'm doing that same crazy escaped JSON thing because I have to tell in a header, I have to tell Dropbox where to put this file, what it's called, and that if something called the same thing already exists, then you should overwrite it, okay? So this is building up all of that JSON that I need. And in our options, we are doing all the usual stuff. We're making a post, we're sending a bearer, we're sending that JSON that we just created. We're saying that this is a special content type, that it's an octet stream. Basically we're saying, hey Dropbox, I'm about to send you a whole lot of binary data. And so we need to use that content type, that very particular content type so that Dropbox is ready to receive binary data. And then the last thing we do is we actually send the data itself. And so you can see that I've done the same thing. I've done ampersand, dollar, dollar file content. This is one of those situations where we have to get around the fact that we don't have access to the file system. I've set file content to the content of the container, and then it's basically just piped back out again. You also have to do that little shuffle. You have to get the content out of the container and into the variable before you've seen that you can't use the file name in here. You can only use variables as parameters in your curl options. The observant amongst you will have noticed in the demo file that there is a second set of Google scripts in there as well. I used the service account as a demonstration in this session, but sometimes you actually want to be able to get the user's authenticated access to their Google account. And there are a set of scripts in there that let you do that as well. Essentially what happens is a web viewer is popped up, the user is asked to log into Google, and then the process runs behind the scenes to get that bearer token back just for that particular user. So have a look at that as well because sometimes you don't wanna go through the process of a service account, you just want the person who's using the system to log into their own Google account. So there's a bunch of scripts in there that provide a demo for that. There's a few bits in there that are more of those creative language moments because we have to trick Google into thinking that we're logging in from a device that has no access, that has no input device in order to let them let us do it. Have a look at the scripts. They are well worth having. And that is that. Questions? I've got seven minutes for questions. Yeah. The question is if you're sending a lot of RAM with a, sorry, if you're sending a large file, if you have to set it to a variable to use lots of, will it use lots of RAM? Absolutely. Yes, if the file is large, it has to be read into a variable which means it will have to go into memory before it can be sent. So if you're trying to do this on an iOS device which doesn't have a lot of memory, you're probably not gonna have tremendously good results with large files for that reason. Yeah. How do I, with the OAuth, how do I pass the client ID and the client secret? So with the service worker account, you don't actually use the client ID and the client secret. You create that jot, that JSON web token. And so that JSON web token contains the user ID in that and it's signed with that user's private key. And so in this particular flavor of OAuth, we're not using those more common tokens and secrets. Okay, so the lady is talking about using OAuth with Basecamp. It will be the same. What you will need to do is find out how they expect to get those. Probably what they want is they want headers.  Maybe concatenate them together, maybe pass one or the other. All of those things, it's very much about reading the documentation for the particular API because although it's all OAuth, it can be implemented in quite a few different ways. If you're running into, the gentleman's asking about problems that they've been running into with SSL and Curl, if you're experiencing SSL problems, it is much more likely to be at the API end that you are calling that potentially the root certificate, that their certificate is signed with, is not recognized by FarmMaker's internal Curl. And to my knowledge, there is nothing that we as users can do about that. I certainly haven't experienced any problems with either APIs I've built myself for the main, the white Google and Dropbox and Twitter that I've worked with. So I can't help you with that, I'm sorry. Minus the gentleman at the front is telling me that minus K is going to help you with that as a Curl option. Apparently minus K will cause force it to ignore an SSL error. Where to try? No, what you're suggesting is correct. In order to access a file that you want to send to FTP, you need to bring it into a container so that you can get it into, well, yeah, that's certainly going to be the easiest way. The alternative is I guess if the file was in FarmMaker's documents directory, then you should be able to load the contents of a file directly into a variable. Okay. Okay, then I would do an insert from URL from your file into a container and then set the variable to the container content and go from there. Okay, apparently there's an issue with the data viewer screwing up binary data and so if you're trying to look at the content of your file in the variable in the data viewer, that may be corrupting the content of the variable. It turns it into a string, obviously, of course it does. It has to, yeah. Okay, thank you very much. Please, if you can do evaluations, it is appreciated. Thank you.