TabSplit provides a simple REST API which provides a JSON interface to programmatically access your data on TabSplit. This can be used to backup your data, or provide additional services based on TabSplit.

API Key

To access the API you need an API key for your application. It is recommended that you create one API Key for all applications. Currently these are only for statistic purposes and to notify all developers of changes to the API. (The API is still in an early phase, but will probably only change in a compatible way since it is already used in our own Android and iOS Applications.)

To generate an API key log visit your account preferences and click on ‘Manage API Keys’: https://tabsplit.com/apikeys/

Using your API Key

For every request detailed below you have to append a GET parameter called apikey containing the value of your api key. e.g. https://tabsplit.com/mobile/friend/?apikey=sFWT3cY6UKtasZkHfhTRR8srrmkFJ2LGsT4tHESr

User Authentication

You must never ever ask the user for his username or password directly. All authentication has to go threw https://tabsplit.com/ while your app should remember the authorization token.

To send an authorization request send the user to the following URL in a browser:

https://tabsplit.com/mobile/login/?next=http://myauthorizationcallback&apikey=1234567

If the user is already logged into TabSplit he will be immediately redirected to the given next URL, otherwise the user will need to sign in (or register) before proceeding. The user will be redirected to the URL given in the ‘next’ attribute with the username and authorization token appended to the URL:

http://myauthorizationcallback?username=h.poul@tapo.at&token=xxxx&userid=1234

You have to parse the GET parameters and store at least username and token. (those parameters might be URL encoded) – You can then use your authorization token to authenticate against the API. To do this you create a GET request to:

https://tabsplit.com/mobile/auth/?apikey=yourapikey&username=usersname&token=authtoken

(make sure to URL encode the username and token because it might contain special characters)

You will receive the following response from the server:

{
  "result": "success"
}

In addition to a Set-Cookie: response header which contains a cookie called ‘sessionid’ which you have to send in all subsequent requests to the server!

Logout / Token invalidation

If the user wants to log out you can invalidate the token and hence log the user out. To do this simply send the user (in a new browser window) to the following URL containing your apikey and the token to invalidate:

https://tabsplit.com/mobile/logout/?apikey=xxxx&token=xxxx

Retrieving Actual Data

So, now we can start fetching data. There are (currently) only three data types: Currencies, Contacts (Friends) and Transactions. All of these objects have unique IDs which are equal for all users, for each request. They are the actual database IDs of those objects, so you can rely on them being unique. – You should also always synchronize your data in the correct order – first currencies, afterwards friends and transactions last.

Currencies

Since there are only a handful of currencies you should simply overwrite all your stored currencies. You can fetch them by sending a GET request to:

https://tabsplit.com/mobile/currency/?apikey=xxxx

The response will be an array of currency objects:

{
    "currency_list": [
        {
            "symbol": "\u20ac",
            "isocode": "EUR",
            "updated": 1321509600.0,
            "id": 1,
            "label": "Euro"
        },
...
}

Contacts

Contacts will include all people the given user has ever had a transaction with.

https://tabsplit.com/mobile/friend/?apikey=xxxxx

The reply will look like:

{
    "friend_list": [
        {
            "username": "herbert.poul@gmail.com",
            "avatar_large": "http://www.gravatar.com/avatar/1389f770c5934980f7f76730da436b0f?s=200",
            "debts": [
                {
                    "currency_id": 1,
                    "currency": "EUR",
                    "amount": 500
                }

            ],
            "email": "herbert.poul@gmail.com",
            "ismyself": true,
            "avatar": "http://www.gravatar.com/avatar/1389f770c5934980f7f76730da436b0f?s=50",
            "full_name": "Herby",
            "id": 1
        },
...
    ]
}

 Transactions

Transactions are at the core of TabSplit, so a user will probably have thousands of transactions. This is why transaction requests are always paged and you can pass in the last status change you synced.

https://tabsplit.com/mobile/transaction/?apikey=xxx&after=0&page=1

The above URL should be your frist request, you will receive the first page including information about the paging:

{
    "num_pages": 31,
    "page": "1",
    "transactions": [
...
    ]
}

What you should do is simply iterating over all pages (sending one request at a time) and remember the biggest value of ‘modifydate’ (see below) – on your next synchronization run simpy pass this modifydate from the last sync into the ‘after=’ GET parameter.

Transactions can be one of three types: Payments (type=payment), Joint Payments (type=jointpayment) or Tabs (type=tab). The first two are basically completely the same:

        {
            "status": "active",
            "currency": 1,
            "modifydate": 1321653336.0,
            "description": "kringers dinner",
            "contacts": [
                {
                    "participanttype": "paid_amount",
                    "tipntaxsplit": 0,
                    "user": 1,
                    "effective_amount": 3050,
                    "pay_amount": 6100
                },
                {
                    "participanttype": "participant",
                    "tipntaxsplit": 0,
                    "user": 5,
                    "effective_amount": -3050,
                    "pay_amount": 0
                }
            ],
            "date": 1321653221.0,
            "debts": [
                {
                    "amount": 3050,
                    "userb": 1,
                    "usera": 5
                }
            ],
            "type": "jointpayment",
            "id": 287
        },

Tabs contain additional properties with the photo of the receipt, paid tip and tax, as well as information about the bill items:

        {
            "status": "active",
            "description": "Eurospar",
            "photo": "/mymedia/photos/2011/11/17/myphoto.png",
            "tax": 0,
            "currency": 1,
            "date": 1321509600.0,
            "is_draft": false,
            "id": 278,
            "rotate": 90.0,
            "modifydate": 1321710055.0,
            "contacts": [
                {
                    "participanttype": "paid_proportionally",
                    "tipntaxsplit": 0,
                    "user": 3,
                    "effective_amount": 384,
                    "pay_amount": 700
                },
                {
                    "participanttype": "participant",
                    "tipntaxsplit": 0,
                    "user": 1,
                    "effective_amount": -384,
                    "pay_amount": 0
                }
            ],
            "items": [
                {
                    "description": "Feinkost",
                    "quick_y": 1732.78601074219,
                    "quick_x": 562.57891845703102,
                    "contacts": [
                        {
                            "user": 3,
                            "pay_amount": 251
                        }
                    ],
                    "amount": 251,
                    "quick_color": "red"
                },
                {
                    "description": "Spar Molke",
                    "quick_y": 1732.78601074219,
                    "quick_x": 638.36846923828102,
                    "contacts": [
                        {
                            "user": 3,
                            "pay_amount": 65
                        }
                    ],
                    "amount": 65,
                    "quick_color": "green"
                },
                {
                    "description": "DeSpar 1,5l",
                    "quick_y": 1734.19360351562,
                    "quick_x": 711.631591796875,
                    "contacts": [
                        {
                            "user": 1,
                            "pay_amount": 29
                        }
                    ],
                    "amount": 29,
                    "quick_color": "blue"
                },
                {
                    "description": "Feinkost",
                    "quick_y": 1731.22265625,
                    "quick_x": 784.95458984375,
                    "contacts": [
                        {
                            "user": 1,
                            "pay_amount": 355
                        }
                    ],
                    "amount": 355,
                    "quick_color": "yellow"
                }
            ],
            "debts": [
                {
                    "amount": 384,
                    "userb": 3,
                    "usera": 1
                }
            ],
            "tip": 0,
            "calculated_total": 700,
            "type": "tab"
        },
        {
            "status": "active",
            "currency": 1,
            "modifydate": 1321653336.0,
            "description": "kringers dinner",
            "contacts": [
                {
                    "participanttype": "paid_amount",
                    "tipntaxsplit": 0,
                    "user": 1,
                    "effective_amount": 3050,
                    "pay_amount": 6100
                },
                {
                    "participanttype": "participant",
                    "tipntaxsplit": 0,
                    "user": 5,
                    "effective_amount": -3050,
                    "pay_amount": 0
                }
            ],
            "date": 1321653221.0,
            "debts": [
                {
                    "amount": 3050,
                    "userb": 1,
                    "usera": 5
                }
            ],
            "type": "jointpayment",
            "id": 287
        },

API is still under development …

.. so just a few words to everyone who managed to get to this point :) – currently most API calls actually work without a (valid) apikey= – this is to ensure backward compatibility with old app versions. You must not rely on that behavior!! we will disable it as soon as most users have updated to our latest app versions. The APIKey is actually there so we can see who uses them, and notify them of changes to the API.