-
Notifications
You must be signed in to change notification settings - Fork 22
Warmshowers RESTful Services for Mobile Apps
Note that all clients should use the canonical name of the host, www.warmshowers.org, in the URL and also use https. Otherwise the redirect comes back as a 404 error.
- CSRF:
/services/session/token
- Log in:
/services/rest/user/login
- Log out:
/services/rest/user/logout
- Search for users by location:
/services/rest/hosts/by_location
- Search for users by keyword:
/services/rest/hosts/by_keyword
- Retrieve user object:
/services/rest/user/<uid>
- Read feedback:
/user/<uid>/json_recommendations
- Create feedback:
/services/rest/node
- Send a private message (not replying to existing):
/services/rest/message/send
- Reply to privatemsg:
/services/rest/message/reply
- Privatemsg unread count:
/services/rest/message/unreadCount
- Retrieve all privatemsgs:
/services/rest/message/get
- Read privatemsg thread:
/services/rest/message/getThread
- Mark privatemsg thread read (or unread):
/services/rest/message/markThreadRead
Following the upgrade to Drupal 7 all authenticated service calls must contain a valid CSRF header - https://www.drupal.org/node/2012982:
Note that Services clients using session authentication now should supply a special X-CSRF-Token header with a token that can be retrieved from http://example.com/services/session/token. This is needed for writing HTTP methods calls (POST, PUT, DELETE).
ALL apps should now request a service token from https://www.warmshowers.org/services/session/token immediately after login and before any further service requests. (Login does not require a token, as there's no session. But even a logout requires a token because it's a session activity.)
GET /services/session/token
Accept: text/plain
To provide a smooth upgrade path from D6 to D7 this request has been included in D6 and responses to both will look the same except that D6 will return a fake token instead. Example response included below.
Status Code: 200 OK
Content-Type: text/plain
Body: KkSi6vREzVqx_M7Hso52z6u_J9rfKspUF7s3kwxYLZc
Apps must now check for a service token as above and include it as an X-CSRF-Token header if one exists:
POST /services/rest/XXXXXXXXXX
X-CSRF-Token: {token_value}
Logs a specified user in and retrieves its profile data on a successful (first) login via this client.
-
URL
/services/rest/user/login
-
Method:
POST
-
Headers
-
Accept: application/json
(Optional. Otherwise, XML is sent)
-
-
URL Params
None
-
Data Params
to be sent as
x-www-form-urlencoded
Required:
-
username=
-
password=
-
-
Success Response:
-
Code:
200 OK
Response: JSON with sessid, session_name, token, and user object.
This information can then be used to authenticate all following requests by adding the following headers:
X-CSRF-Token: <token> Cookie: <session_name>=<sessid>
Content: (Example)
JSON:
{ "sessid": "", "session_name": "", "token": "", "user": { "uid": "", "name": "", "theme": "", "signature_format": "", "created": "", // Unix timestamp as a String "access": "", // Unix timestamp as a String "login": 1460103119, // Unix timestamp as an Integer "status": "", "timezone": "", "language": "", "picture": { "fid": "", "uid": "", "filename": "", "uri": "", "filemime": "", "filesize": "", "status": "", "timestamp": "", "url": "" }, "fullname": "", "notcurrentlyavailable": "", "fax_number": "", "mobilephone": "", "workphone": "", "homephone": "", "preferred_notice": "", "maxcyclists": "", "storage": "", "motel": "", "campground": "", "bikeshop": "", "comments": "", "shower": "", "kitchenuse": "", "lawnspace": "", "sag": "", "bed": "", "laundry": "", "food": "", "languagesspoken": "", "URL": "", "becomeavailable": "", "set_unavailable_timestamp": "", "set_available_timestamp": "", "last_unavailability_pester": "", "hide_donation_status": "", "email_opt_out": "", "street": "", "additional": "", "city": "", "province": "", "postal_code": "", "country": "", "latitude": "", "longitude": "", "source": "", "comment_notify_settings": { "uid": "", "node_notify": "", "comment_notify": "" }, "privatemsg_disabled": false, "profile_image_mobile_profile_photo_std": "", "profile_image_profile_picture": "", "profile_image_mobile_photo_456": "", "profile_image_map_infoWindow": "" } }
XML:
<?xml version="1.0" encoding="utf-8"?> <result> <sessid></sessid> <session_name></session_name> <token></token> <user> <uid></uid> <name></name> <theme></theme> <signature_format></signature_format> <created></created> <access></access> <login></login> <status></status> <timezone></timezone> <language></language> <picture> <fid></fid> <uid></uid> <filename></filename> <uri></uri> <filemime></filemime> <filesize></filesize> <status></status> <timestamp></timestamp> <url></url> </picture> <fullname></fullname> <notcurrentlyavailable></notcurrentlyavailable> <fax_number></fax_number> <mobilephone></mobilephone> <workphone></workphone> <homephone></homephone> <preferred_notice></preferred_notice> <maxcyclists></maxcyclists> <storage></storage> <motel></motel> <campground></campground> <bikeshop></bikeshop> <comments></comments> <shower></shower> <kitchenuse></kitchenuse> <lawnspace></lawnspace> <sag></sag> <bed></bed> <laundry></laundry> <food></food> <languagesspoken></languagesspoken> <URL></URL> <becomeavailable></becomeavailable> <set_unavailable_timestamp></set_unavailable_timestamp> <set_available_timestamp></set_available_timestamp> <last_unavailability_pester></last_unavailability_pester> <hide_donation_status></hide_donation_status> <email_opt_out></email_opt_out> <street></street> <additional></additional> <city></city> <province></province> <postal_code></postal_code> <country></country> <latitude></latitude> <longitude></longitude> <source></source> <comment_notify_settings> <uid></uid> <node_notify></node_notify> <comment_notify></comment_notify> </comment_notify_settings> <privatemsg_disabled></privatemsg_disabled> <profile_image_mobile_profile_photo_std></profile_image_mobile_profile_photo_std> <profile_image_profile_picture></profile_image_profile_picture> <profile_image_mobile_photo_456></profile_image_mobile_photo_456> <profile_image_map_infoWindow></profile_image_map_infoWindow> </user> </result>
-
-
Error Response:
-
Code:
401 Unauthorized : Wrong username or password.
Content:
JSON:
[ "Wrong username or password." ]
XML:
<?xml version="1.0" encoding="utf-8"?> <result>Wrong username or password.</result>
-
Code:
401 Unauthorized : Missing argument username
Content:
JSON:
[ "Missing required argument username" ]
XML:
<?xml version="1.0" encoding="utf-8"?> <result>Missing required argument username</result>
-
Code:
401 Unauthorized : Missing argument password
Content:
JSON:
[ "Missing required argument password" ]
XML:
<?xml version="1.0" encoding="utf-8"?> <result>Missing required argument password</result>
-
Code:
406 NOT ACCEPTABLE : Already logged in as <username>.
Content:
JSON:
[ "Already logged in as <username>." ]
XML:
<?xml version="1.0" encoding="utf-8"?> <result>Already logged in as <username>.</result>
-
Logs the currently logged in user out.
-
URL
/services/rest/user/logout
-
Method:
POST
-
Headers
-
Accept: application/json
(Optional. Otherwise, XML is sent) -
Cookie: <session_name>=<session_id>
(Retrieved during login) -
X-CSRF-Token: <token>
(Also retrieved during login)
-
-
URL Params
None
-
Data Params
None
-
Success Response:
-
Code: 200 OK
Content: (Example)
JSON:
[ true ]
XML:
<?xml version="1.0" encoding="utf-8"?> <result>1</result>
-
-
Error Response:
-
Code:
401 Unauthorized : CSRF validation failed
Content:
JSON:
[ "CSRF validation failed" ]
XML:
<?xml version="1.0" encoding="utf-8"?> <result>CSRF validation failed</result>
-
Code:
406 NOT ACCEPTABLE : User is not logged in.
Content:
JSON:
[ "User is not logged in." ]
XML:
<?xml version="1.0" encoding="utf-8"?> <result>User is not logged in.</result>
-
POST /services/rest/hosts/by_location
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
Post parameters:
minlat maxlat minlon maxlon centerlat centerlon limit
offset and limit parameters allow paging through the results. The results include a total count.
POST /services/rest/hosts/by_keyword
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
Post parameters:
keyword offset limit
GET /services/rest/user/<UID>
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
GET /user/<uid>/json
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
GET /user/<uid>/json_recommendations
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
(Note: I had to do drush vset services_node_save_button_trust_referral_resource_create "Submit"
to make this work)
POST /services/rest/node
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
Post parameters:
node[type]=trust_referral
node[field_member_i_trust][0][uid][uid]=<username> (NOTE not the uid, the username instead)
node[field_rating][value]=Postive|Neutral|Negative
node[body]=<body of feedback>
node[field_guest_or_host][value]=Guest|Host|Met Traveling|Other
node[field_hosting_date][0][value][year]=<year>
node[field_hosting_date][0][value][month]=<numeric month, 1-12>
POST /services/rest/message/send
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
Post parameters:
recipients=comma-delimited set of username(s) [Not UID]
subject=
body=
POST /services/rest/message/reply
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
Parameters thread_id= body=
POST /services/rest/message/unreadCount
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
POST /services/rest/message/get
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
POST /services/rest/message/getThread
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
Parameters thread_id=
POST /services/rest/message/markThreadRead
Accept: application/json
Cookie: <session_name>=<sessid> (obtained from login)
Parameters thread_id= status=[0|1] (OPTIONAL, defaults to 0==READ. 1 means UNREAD)
** experimental, not in effect yet **
Access inbox: GET /services/rest/message
Access a single message: GET /services/rest/message/MESSAGE_ID
Create a new message thread (if thread_id is empty) or reply to a thread (if thread_id is provided) POST /services/rest/message
with
- body
- recipients (username)
- subject
- thread_id