Help adding a datapoint from Tasker using Beeminder API


#1

Hi guys, I could really use some help grokking the API - more specifically how to http POST to the datapoints endpoint to add a datapoint - I’ve lost four hours to this and I’m pulling my hair out.

I’m trying to do this from Tasker, and I originally set up an AutoWeb API configuration to do so, but when I remembered that Tasker has a built in http-post action that will suffice, as I’m not doing anything too complex, so Autoweb was probably overkill.

Looking at the AutoWeb logs, I’m fairly certain that what it is sending is equivalent to curl -X POST https://www.beeminder.com/api/v1/users/me/goals/test/datapoints.json?auth_token=XXXX -d value=1.
But the result I get is simply all the datapoints for this goal (without the new point I’m trying to add), as though I had called it using GET instead, and it does not add any new datapoints to he goal. There is no error message - the API responds with the datapoints for the goal, with no complaints.

I get the same thing trying it with the Tasker http-post action.

Weirdly, when I try to test it directly using curl on windows I got a “301 Moved Permanently” Error, which I’m almost certain is a red herring and due to some part of my local curl config being borked. But it made it very hard to test the correct construction!

Edit: I just figured this out - I was missing the www. This fixed posting via CURL, but I still cant get it to work via tasker, even after adding the www. It’s als weired that through tasker it returned the datapoints even without the www, and not a 301 error

When doing a POST to datapoints.json, does anyone know what parts should be parameters, what are headers, and what is in the body, and also what format the body takes?

Or better yet, does anyone out there use Autoweb and want to take a look at my API config? I have added it to AutoWeb’s shared directory.


#2

OK, I have managed to get it to add a datapoint via Autoweb after adding in the www to the address, and by submitting the value parameter as a “Query” type input in autoweb- ie url encoded. But it only seems to use one of the variables this way - my comment input was ignored.

Posting with a message body of eg value=1&comment=test causes it to flame out with some new and different error.


#3

I’ve used the API a lot from both curl and Python, and it basically works the way it says it does.

When you do something like:

curl -X POST https://www.beeminder.com/api/v1/users/alice/goals/weight/datapoints.json
-d auth_token=abc123
-d timestamp=1325523600
-d value=130.1
-d comment=sweat+a+lot+today

per the docs, you’re using curl’s -d, which basically sends it like a webbrowser does.

  -d, --data <data>

         (HTTP) Sends the specified data in  a  POST  request  to  the  HTTP

         server,  in the same way that a browser does when a user has filled

         in an HTML form and presses the submit button. This will cause curl

         to  pass  the  data  to  the server using the content-type applica-

         tion/x-www-form-urlencoded.  Compare to -F, --form.

Lemme do one with curl and get all the headers for you:

$ curl -v -X POST https://www.beeminder.com/api/v1/users/adamwolf/goals/coffeeless_days/datapoints.json -d auth_token=REDACTED -d value=1 -d comment=helloworld

Note: Unnecessary use of -X or --request, POST is already inferred.

  • Trying 23.92.16.25…

  • TCP_NODELAY set

  • Connected to www.beeminder.com (23.92.16.25) port 443 (#0)

  • ALPN, offering h2

  • ALPN, offering http/1.1

  • Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH

  • successfully set certificate verify locations:

  • CAfile: /etc/ssl/cert.pem

CApath: none

  • TLSv1.2 (OUT), TLS handshake, Client hello (1):

  • TLSv1.2 (IN), TLS handshake, Server hello (2):

  • TLSv1.2 (IN), TLS handshake, Certificate (11):

  • TLSv1.2 (IN), TLS handshake, Server key exchange (12):

  • TLSv1.2 (IN), TLS handshake, Server finished (14):

  • TLSv1.2 (OUT), TLS handshake, Client key exchange (16):

  • TLSv1.2 (OUT), TLS change cipher, Client hello (1):

  • TLSv1.2 (OUT), TLS handshake, Finished (20):

  • TLSv1.2 (IN), TLS change cipher, Client hello (1):

  • TLSv1.2 (IN), TLS handshake, Finished (20):

  • SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384

  • ALPN, server accepted to use http/1.1

  • Server certificate:

  • subject: OU=Domain Control Validated; CN=*.beeminder.com

  • start date: Sep 15 01:00:49 2015 GMT

  • expire date: Sep 15 01:00:49 2018 GMT

  • subjectAltName: host “www.beeminder.com” matched cert’s “*.beeminder.com”

  • issuer: C=BE; O=GlobalSign nv-sa; CN=AlphaSSL CA - SHA256 - G2

  • SSL certificate verify ok.

> POST /api/v1/users/adamwolf/goals/coffeeless_days/datapoints.json HTTP/1.1

> Host: www.beeminder.com

> User-Agent: curl/7.54.0

> Accept: /

> Content-Length: 58

> Content-Type: application/x-www-form-urlencoded

>

  • upload completely sent off: 58 out of 58 bytes

< HTTP/1.1 200 OK

< Content-Type: application/json; charset=utf-8

< Transfer-Encoding: chunked

< Connection: keep-alive

< Status: 200 OK

< Cache-Control: max-age=0, private, must-revalidate

< X-XSS-Protection: 1; mode=block

< X-Request-Id: 7b49bcb9-0d80-45d9-be78-50e5acf5563e

< X-UA-Compatible: chrome=1

< ETag: “415d47182e8c853ae81f4d45f04a76d7”

< X-Frame-Options: SAMEORIGIN

< X-Runtime: 0.213489

< X-Content-Type-Options: nosniff

< Date: Wed, 08 Aug 2018 13:37:04 GMT

< Set-Cookie: _beeminder_session=REDACTED; path=/; HttpOnly

< X-Powered-By: Phusion Passenger Enterprise 5.1.10

< Server: nginx/1.12.1 + Phusion Passenger 5.1.10

<

{“timestamp”:1533735424,“value”:1.0,“comment”:“helloworld”,“id”:“5b6af200bfec0316940003e7”,“updated_at”:1533735424,“requestid”:null,“canonical”:“08 1 “helloworld””,“fulltext”:“2018-Aug-08 entered at 08:37 via api”,“origin”:“api”,“daystamp”:“20180808”,“status”:“created”}%

I think that without another user who has already done this, your best bet may be to email support. They miiight be able to take a look at your API calls. Maybe it’s something silly like Autoweb is sending GETs instead?

Please let us know if you figure it out!

On Wed, Aug 8, 2018 at 8:06 AM imoatama <beeminder@discoursemail.com> wrote:

imoatama
August 8

OK, I have managed to get it to add a datapoint via Autoweb after adding in the www to the address, and by submitting the value parameter as a “Query” type input in autoweb- ie url encoded. But it only seems to use one of the variables this way - my comment input was ignored.

Posting with a message body of eg value=1&comment=test causes it to flame out with some new and different error.

Visit Topic or reply to this email to respond.

You are receiving this because you enabled mailing list mode.

To unsubscribe from these emails, click here.


My list of bugs and feature requests
#4

I would like help with this as well, specifically how to add a datapoint that’s a variable. See bugs 3 and 16 on my list: My list of bugs and feature requests


#5

I really appreciate the response, but it’s more the body, not the headers, that are of interest.

I’ve tried everything I can think of, but submitting with a body of
value=1&comment=blah

doesn’t give the expected response using either autoweb or tasker http-post.


#6

It seems that both tasker http-post and Autoweb just don’t work as expected. I eventually got it working using another Tasker plugin “RESTask for Tasker”, which works correctly.

Submitting a body of auth_token=xxxxxxxx&value=1&comment=blah using Tasker http-post resulted in errors. Submitting exactly the same body using RESTask worked.


#7

Awesome! If you don’t mind, do you want to share what you are doing? I have a bunch of things I do on my phone that I would love to tie I to Beeminder, if it wasn’t too finicky.


#8

Were you able to submit a variable?
Has anyone tried Automate? It works with the Tasker plugin and I was able to use it to tell Beeminder to start and stop the timer.


#9

I was able to submit a variable, using the RESTask for Tasker plugin - it was quite easy with that actually.

If I find time I will write a separate post with a short howto, but basically it’s pretty simple, you set the path to the API endpoint for the goal you want to update, and you set parameters (scroll across at the top of the RESTask UI, and there are tabs, they’re easy to miss) according to what you want to submit - just make sure that one of them is your auth_token.


#10

But how do I get the variable from another app and send it to Tasker? I’d love to read your howto.


#11

That sounds more like a question about how to use Tasker, not how to use the Beeminder API, which is sort of out of the scope of what I was focused on getting working. I recommend you read up on how Tasker works and how to use it.

But generally, unless the app you are using has a Tasker integration, you are fairly limited in how you can get data from another app and use it in Tasker. If you can be more specific about what you’re trying to do I can probably help though.


#12

Sorry, I should have been clearer. I have another app with a Tasker integration. I want to take a variable from that app and feed it into Beeminder.


#13

OK, then you should:

  1. Install RESTASK for Tasker in Google Play.

  2. Create a new Task in Tasker

  3. Add an action using your app integration that pulls whatever it is you need into a variable, say %appinfo.

  4. Add a RESTask action, and configure the following:

  • Under the settings tab:

  • Under the parameters tab, add the following parameters:

    • auth_token: <yourauthtoken>
    • value: %appinfo (or whatever variable you set using the other app integration)
    • comment: <yourcommenthere>

By default Beeminder will use the time of submission as the timestamp, but if you want to set it yourself you can add either a timestamp or a daystamp parameter too.


#14

Crossposted from another thread,

You can submit directly to the API using a Tasker plugin called RESTask. I’ve provided an XML export of the Tasker task below - you simply need to replace the parts surrounded by tildes ~ with your values. You can also use Tasker variables in the parameters fields.

XML for Tasker task to submit to Beeminder API
<TaskerData sr="" dvi="1" tv="5.2.bf1">
<Task sr="task60">
<cdate>1536523195550</cdate>
<edate>1537846704633</edate>
<id>60</id>
<nme>Submit To Beeminder API</nme>
<pri>100</pri>
<Action sr="act0" ve="7">
<code>554915598</code>
<Bundle sr="arg0">
<Vals sr="val">
<bundle_header></bundle_header>
<bundle_header-type>java.lang.String</bundle_header-type>
<bundle_param>auth_token ~your_token_here~||value ~your_value_here~||daystamp ~datapoint_date_here~||comment ~your_comment_here~</bundle_param>
<bundle_param-type>java.lang.String</bundle_param-type>
<com.freehaha.restask.BASICAUTH></com.freehaha.restask.BASICAUTH>
<com.freehaha.restask.BASICAUTH-type>java.lang.String</com.freehaha.restask.BASICAUTH-type>
<com.freehaha.restask.BODY></com.freehaha.restask.BODY>
<com.freehaha.restask.BODY-type>java.lang.String</com.freehaha.restask.BODY-type>
<com.freehaha.restask.CERT></com.freehaha.restask.CERT>
<com.freehaha.restask.CERT-type>java.lang.String</com.freehaha.restask.CERT-type>
<com.freehaha.restask.CERTPATH>empty</com.freehaha.restask.CERTPATH>
<com.freehaha.restask.CERTPATH-type>java.lang.String</com.freehaha.restask.CERTPATH-type>
<com.freehaha.restask.CHUNKED>n</com.freehaha.restask.CHUNKED>
<com.freehaha.restask.CHUNKED-type>java.lang.String</com.freehaha.restask.CHUNKED-type>
<com.freehaha.restask.COOKIE>%rtck</com.freehaha.restask.COOKIE>
<com.freehaha.restask.COOKIE-type>java.lang.String</com.freehaha.restask.COOKIE-type>
<com.freehaha.restask.COOKIEJAR>restask</com.freehaha.restask.COOKIEJAR>
<com.freehaha.restask.COOKIEJAR-type>java.lang.String</com.freehaha.restask.COOKIEJAR-type>
<com.freehaha.restask.CUSTOMBODY>n</com.freehaha.restask.CUSTOMBODY>
<com.freehaha.restask.CUSTOMBODY-type>java.lang.String</com.freehaha.restask.CUSTOMBODY-type>
<com.freehaha.restask.CUSTOMECERT>n</com.freehaha.restask.CUSTOMECERT>
<com.freehaha.restask.CUSTOMECERT-type>java.lang.String</com.freehaha.restask.CUSTOMECERT-type>
<com.freehaha.restask.HEADER>%rthdr</com.freehaha.restask.HEADER>
<com.freehaha.restask.HEADER-type>java.lang.String</com.freehaha.restask.HEADER-type>
<com.freehaha.restask.HOST><com.freehaha.restask.HOST>https://www.beeminder.com/api/v1/users/me/goals/~yourgoalhere~/datapoints.json</com.freehaha.restask.HOST>
<com.freehaha.restask.HOST-type>java.lang.String</com.freehaha.restask.HOST-type>
<com.freehaha.restask.KEEP_COOKIE>n</com.freehaha.restask.KEEP_COOKIE>
<com.freehaha.restask.KEEP_COOKIE-type>java.lang.String</com.freehaha.restask.KEEP_COOKIE-type>
<com.freehaha.restask.OUTPUT_FILE>beemind_API.out</com.freehaha.restask.OUTPUT_FILE>
<com.freehaha.restask.OUTPUT_FILE-type>java.lang.String</com.freehaha.restask.OUTPUT_FILE-type>
<com.freehaha.restask.REDIRECT>y</com.freehaha.restask.REDIRECT>
<com.freehaha.restask.REDIRECT-type>java.lang.String</com.freehaha.restask.REDIRECT-type>
<com.freehaha.restask.RESPONSE>%rtres</com.freehaha.restask.RESPONSE>
<com.freehaha.restask.RESPONSE-type>java.lang.String</com.freehaha.restask.RESPONSE-type>
<com.freehaha.restask.RETCODE>%rtcode</com.freehaha.restask.RETCODE>
<com.freehaha.restask.RETCODE-type>java.lang.String</com.freehaha.restask.RETCODE-type>
<com.freehaha.restask.SYNCCALL>n</com.freehaha.restask.SYNCCALL>
<com.freehaha.restask.SYNCCALL-type>java.lang.String</com.freehaha.restask.SYNCCALL-type>
<com.freehaha.restask.TIMEOUT>30</com.freehaha.restask.TIMEOUT>
<com.freehaha.restask.TIMEOUT-type>java.lang.String</com.freehaha.restask.TIMEOUT-type>
<com.freehaha.restask.TYPE>POST</com.freehaha.restask.TYPE>
<com.freehaha.restask.TYPE-type>java.lang.String</com.freehaha.restask.TYPE-type>
<com.twofortyfouram.locale.intent.extra.BLURB>REST call</com.twofortyfouram.locale.intent.extra.BLURB>
<com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type>
<net.dinglisch.android.tasker.RELEVANT_VARIABLES>&lt;StringArray sr=""&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;%rtcode
Response code&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0&gt;&lt;_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;%rtres
Response body&lt;/_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1&gt;&lt;/StringArray&gt;</net.dinglisch.android.tasker.RELEVANT_VARIABLES>
<net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>
<net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>com.freehaha.restask.TYPE com.freehaha.restask.HOST com.freehaha.restask.TIMEOUT com.freehaha.restask.BASICAUTH com.freehaha.restask.CUSTOMBODY com.freehaha.restask.BODY com.freehaha.restask.CUSTOMECERT com.freehaha.restask.CERTPATH com.freehaha.restask.SYNCCALL com.freehaha.restask.HEADER com.freehaha.restask.COOKIE com.freehaha.restask.COOKIEJAR com.freehaha.restask.KEEP_COOKIE com.freehaha.restask.REDIRECT com.freehaha.restask.CHUNKED com.freehaha.restask.OUTPUT_FILE bundle_param bundle_header</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>
<net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>java.lang.String</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>
<net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled>
<net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type>
</Vals>
</Bundle>
<Str sr="arg1" ve="3">com.freehaha.restask</Str>
<Str sr="arg2" ve="3">com.freehaha.restask.EditActivity</Str>
<Int sr="arg3" val="30"/>
</Action>
</Task>
</TaskerData>