
| [Top] | [Contents] | [Index] |
A pdf version of this document is available: http://yootles.com/api.pdf.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Yootles is a system for declaring IOUs between people on a social network. It is designed to allow people to jot down who paid for dinner or other transactions that people often want to but forget to keep track of. But it's useful for much more. It can be used for things like tracking rent payments, sharing utility bills, even tracking interest on personal loans. And it supports non-monetary currencies. Yootles in fact refers to the seminal currency in the system: an abstract measure of happiness or utility used in a suite of forthcoming applications including voting and wagering and various incentive mechanisms.
The next incarnation of the Yootles IOU system will support arbitrary IOU routing (see http://yootles.com/trustnets.pdf). The current version supports bilateral accounting of IOUs with some advanced accounting features. It will always be possible to export your transaction history in an open format so you will never be tied to any particular implementation. (You wouldn't use Flickr without assurance that you could always retrieve your photos--Yootles treats your IOUs the same way.) In fact, your IOUs are always stored exactly as you type them so you can always independently verify everything the Yootles system tells you about your accounts and your balances.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
A user is a person who logs in to the Yootles system.
An account is an entity that can issue and receive IOUs.
A user may have several accounts, perhaps subaccounts to keep track of different spending categories, or accounts for friends who are not actual users in the system.
Accounts may also be created for purposes such as charity fundraising that aren't tied to any particular user.
All accounts are specified by an account group and an account name within that group, separated by a colon (e.g., "smithfamily:alice").
New users creating their first accounts need not know about the concept
of groups--in that case a default account is created by making the
group name the username and the account name the user's initial(s) or
first name, e.g., "alice:ac" or "alice:alice".
Note that, while group names must be globally unique, account names must
only be unique within groups.
For example, jets:bob and sharks:bob are entirely distinct accounts.
By default, all users have access to all accounts and simply issuing an IOU to or from a nonexistent account creates that account.
We describe this open access philosophy and how to limit it, along with
other relationships between users and accounts
below (see section Access Control).
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
At its most basic an IOU is a simple transfer from one account to another. We call this an atomic IOU. Yootles also supports two generalizations: repeating IOUs and multilateral IOUs. Fundamentally, all IOUs are broken down into atomic IOUs, but Yootles does not store them this way (1) because this would clutter the transaction history and in fact there is additional information implicit in the fact that an IOU came from a repeating or multilateral IOU that would be lost if all IOUs were atomized.
From a user's point of view a settlement is the opposite of an IOU. The Yootles system, however, has no conception of settlements. When someone makes good on (settles) a past IOU it is recorded as simply another IOU in the opposite direction. We leave it to the user interface to add whatever layer is deemed appropriate to make that intuitive. (Note that this invariably trips people up.)
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
A repeating IOU specifies a time interval that the IOU automatically repeats, e.g., "every 2 weeks". IOUs can repeat indefinitely or end at a specified time. When an end time is specified it is not taken to be the time of the last IOU but rather a time after which no further IOUs will be generated. The last repeated IOU is prorated for the amount of time between the last IOU and the end time, compared to the repeat interval. For example, a Y$60 IOU that repeats semiannually, issued on 2008 January 1 and ending 2009 April 1 will be atomized into:
Note that, consistent with the above interpretation of end date, if the end date coincides with the last IOU date then the last payment will be prorated to zero. The reason for this interpretation of the end date is that it preserves the property that the total amount paid divided by the elapsed time between start and end is always exactly equal to the IOU amount. User interfaces may want to highlight the prorated amount of the last payment to avoid any confusion. The API makes it easy to do that. (2)
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Multilateral IOUs are IOUs that are shared between some set of accounts. Instead of specifying a single issuer account and recipient account, either or both of these may be a set of accounts, specified as mathematical expressions in which the accounts are treated as variables and the corresponding coefficients indicate proportions by which the IOU amount is split between the issuers or recipients. This turns out to be a simple and elegant way to specify group IOUs, as the following examples illustrate:
| Multilateral IOU | Atomic IOUs |
|---|---|
|
|
| Multilateral IOU | Atomic IOUs |
|---|---|
|
|
I.e., Alice nets -10, Bob nets -20, and Carol nets +30.
| Multilateral IOU | Atomic IOU |
|---|---|
|
which becomes
|
I.e., Alice nets -10, Bob nets -10, Carol nets +10, and Deb nets +10.
In the following example, Alice and Bob have dinner and Alice orders a $7 dish, Bob a $9 dish, and the bill with tax/tip comes to $20. They each chip in $10.
| Multilateral IOU | Atomic IOUs |
|---|---|
|
which becomes
|
The above has a net effect of 1.25 from Bob to Alice. This is the precise amount, allocating tax/tip proportional to meal costs, that Bob owes Alice after she put in more than her share--10 instead of 7/16*20--for the meal).
The last example shows how--once you're used to the powerful and perhaps initially intimidating language for expressing them--you can track complicated multilateral IOUs by specifying very concisely the nature of the events that occasioned them. This will turn out to be valuable for various Yootles decision and prediction mechanisms. Interfaces can sugar-coat this underlying language in various ways for less sophisticated users. In the initial release of the Yootles Facebook application, the full power of multilateral IOUs will not be exposed at all.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Currencies can be created for anything--hours of babysitting, borrowed books, beers, ounces of gold, points, kudos, and even, of course, units of happiness. All currencies are publicly available to everyone. There is no such thing as a private currency, just obscure ones. The Yootles system does not track exchange rates between any currencies. It tracks your balances for each currency you have dealt with and keeps them entirely separate.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Every pair of accounts at every moment in time has an interest rate (which applies to all currencies) that causes positive balances to become more positive and negative balances to become more negative (or vice versa if the interest rate is negative, which is allowed (4)). Anyone who can issue IOUs for both accounts (see the Access Control section below) can change the interest rate for an account pair at any time. Interest rates can also be changed retroactively.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Every directed pair of accounts has a credit limit for each currency, initially zero. For example, Alice can state that she trusts Bob up to 10 yootles, meaning that she is willing to issue her own IOU of 10 yootles in exchange for receiving an IOU from Bob for the same amount, assuming the interest rate differential is not unfavorable. Credit limits are the basis of automatic IOU routing (see http://yootles.com/trustnets.pdf). Credit limits can be specified in the current Yootles system but automatic IOU routing is not done. (5)
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
As explained above, there is a strict conceptual separation between
Yootles accounts and users.
All of the back-end processing is done strictly in terms of accounts, and users can manipulate those accounts according to an access policy.
Specifically, there are six flags that specify a given user's
relationship to a given account
(we use a running example with alice as a representative user
accessing the account jets:bob):
root -
Having root access on an account means being able to change access
settings for other users.
If user alice has root access on jets:bob then alice
was the original creator of jets:bob or had root status conferred
by another root user.
By definition, only root users can change this flag, with one exception:
if no user has root access to an account (i.e., the sole root user revoked
their own root access) then the account is considered an orphan and
any user can set root access.
(It is thus safer to transfer root access by setting it for the new user
before revoking it for the old user.)
view -
Having view access on an account means being able to view IOUs involving
that account.
For example, if view is true for <alice, jets:bob>
then alice can view all IOUs involving jets:bob.
Only root users can change this flag.
ctrl -
Having control access on an account means being able to issue IOUs
from that account.
With ctrl access, alice can issue IOUs from jets:bob.
Note that this is the default for all users and all accounts.
Only root users can change this flag.
main -
When main is set to true for a user/account pair, it indicates
that that account is the user's main account.
Designating account jets:bob as alice's main account
means it is the one referred to by the special account designating
syntax "[alice]".
I.e., square brackets around a username is syntactic
sugar for the main account of that user.
Anyone with view and ctrl access can make an account their
main account unless it is already the main account of someone else.
A user need not have a main account (and won't have one until they
choose or create one) in which case using the square bracket designation for
that user will be treated as an error.
mine -
This field is typically treated as boolean but can in fact be set to any
real number in [0,1] where 0 ("not mine") is interpreted as false and 1
("all mine") as true.
For a user to have a mine setting of x for an account means
that a fraction x of that account's balance should be included in the user's net balance.
Conceptually, this field indicates what fraction of an account belongs
to the user.
Under this interpretation, the mine settings for all users for a
given account should sum to one, though this is not enforced.
As an example, if mine=1 for <alice, jets:bob> then
account jets:bob is treated as alice's subaccount and is included
in alice's net balance.
Anyone with view and ctrl access can make an account their
subaccount (to any degree).
ntfy -
Having "notify access" on an account means you receive notifications
when the account receives or issues an IOU.
(E.g., alice would be notified whenever jets:bob receives
or issues an IOU.)
Only root users can change this flag, except that anyone can turn off notifications for themselves, i.e., unsubscribe.
The following constraints should hold among these flags:
main ⇒ view & ctrl (you should always be able to view and control your main account)
mine > 0 ⇒ view & ctrl (you should always have control over your subaccounts)
main ⇒ mine = 1 (your main account should always show up in your net balance)
Note that ctrl does not imply view.
(The default is ctrl and view access but it is possible to
have only ctrl access but not view access to an account.)
Also note that mine does not imply root since anyone can claim an account as their main account or subaccount.
This means you can do most of what you might want with no permission but will still have to ask for permission to change the access of others.
Notification is critical to such a liberal access policy.
Evil Eve can issue IOUs to herself from random accounts all day long but
users who manage those accounts can quickly repudiate them.
Finally, consider the case of the user alice creating the account jets:bob for Bob who is not currently a user.
No setup is required on Alice's part--issuing an IOU to or from jets:bob automatically creates that account.
But instead of or in addition to specifying an account name, a front-end
using the Yootles API may allow Alice to specify Bob's email address.
In that case the system will create a new user and a new account for
Bob and invite Bob to claim the account.
Bob can change his username and account name if he wants
(6)
but if he only has one account the interface should keep him blissfully ignorant of the concept of multiple accounts at all.)
The following flags will be set for Alice and Bob and the new account (which we'll continue to refer to as jets:bob for simplicity):
alice, account jets:bob
root = yes (since Alice created the account)
view = yes
ctrl = yes (this is the default for all users and accounts)
main = no
mine = 0
ntfy = yes
bob (though Bob does not yet have a username), account jets:bob
root = yes (since Alice specified a new user for this account, we assume she means to confer root status)
view = yes
ctrl = yes
main = no (only Bob can set an account to be his main account)
mine = 1
ntfy = yes (this will be moot if Alice did not provide an email address for Bob)
Future versions of Yootles may support a richer language for blocking classes of users.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
The Yootles Facebook application is currently the primary client for the Yootles API.
Suppose Alice is a Facebook user who just installed the Yootles application.
Alice would like to give Bob an IOU.
The system should prompt Alice to choose a Yootles username (with an
auto-generated default available based on her full name) and a nickname
for herself, defaulting to her initials (possibly even a single initial).
Suppose her username is alice and her nickname is alc.
The system creates the username alice and creates the account alice:alc as Alice's main account.
Note that, although the group alice is created in this process, Alice will
never need to know about the concept of groups as long as she remains a
casual user.
Next, Alice enters the amount and a short string giving the reason for the IOU and selects her friend Bob as the recipient via the usual "start typing a friend's name" interface.
More complicated IOUs can be issued by setting other fields but everything except the amount, the recipient, and the reason have reasonable defaults (see section owe (adding or modifying an IOU) for details).
If Bob has a main account already, say bob:b, the system uses it and
Alice sees in her transaction history an entry like "123 from alice:alc
to bob:b for `lunch' on 2007-12-04" (possibly with the "alice:"
group prefix redacted).
Otherwise, Alice is prompted to choose a nickname for Bob, say "bob",
which the system will use to construct an account for Bob using Alice's
group, e.g., alice:bob.
In this case there is no reason to show the group prefixes and Alice
sees an entry like "123 from alice to bob for `lunch' on 2007-12-04".
Bob will be notified that Alice has given him an IOU (as will Alice), with a link to edit it or delete it,(7) but the system requires no action from Bob.
Carol might go through the same process as Alice, leaving Bob with two accounts, alice:bob and carol:bob.
Both accounts will be marked as belonging to Bob (via the mine field in the access table) but neither will be marked as his main account unless he chooses one.
Or he can create a new account altogether and consolidate the balances
and close the accounts alice:bob and alice:carol (see section merge (merging/renaming accounts)).
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
This case is similar to the previous one but instead of selecting Bob via the Facebook-style interface, Alice supplies an email address for Bob. The system checks if there is a Yootles user with that email address and, if so, whether that user has a main account. If so, the email address is all that is required. Otherwise, Alice chooses a nickname for Bob and the process continues as above with a new user created for Bob with whatever contact information, such as email address, that Alice provided. Alice can also use the same process without providing an email address or any contact information for Bob. In this case the account created for Bob amounts to merely a subaccount of Alice's. Bob may or may not end up taking control of it in the future.
The notifications Bob receives will be something like: "Alice has given you an IOU for ___. Click here to take control of your account or click here to stop receiving these notifications." Nothing prevents Alice from issuing and IOU from Bob as well (equivalent to a negative IOU to Bob), in which case the notification will be: "Alice has marked you as owing her ___. Click here to take control of your account (or edit/repudiate this IOU) or click here to stop notifications."
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Dan announces to his friends that he is fund-raising for the American Lung Association (ALA) and that if anyone would like to donate yootles just transfer them to the account yooniversal:ala.
(8)
Yootles have no exchange rate with real money but Dan pledges to his friends
that however many yootles end up in the ala account, he will donate that
many dollars to the ALA (and then transfer the yootles from yooniversal:ala to
himself).
In effect, his friends are promising him influence in decisions, potential bragging rights in wagers, and other forms of "yootility" in exchange for his donation to a worthy charity.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Housemates can create a common group and easily do shared accounting within that group (as well as with people outside of it).
Multilateral IOUs are useful for this.
Suppose the housemates each have accounts: elmstreet:alice, elmstreet:bob, elmstreet:carol.
A typical IOU might be entered as "$100 from `alice+bob+3carol' to bob for `water bill that Bob paid for and for which Carol used 3 times as much water as anyone else' in the group elmstreet."
The previous sections on multilateral IOUs and repeating IOUs (useful
for rent payments!) have more details about group accounting
(see section IOUs).
Note that this kind of group accounting does not require buy-in from all the members.
We have found that it is typically a minority of people in a group who are motivated to track IOUs but that almost everyone is happy to be included so long as they aren't relied upon to do anything.
By default, accounts such as elmstreet:carol are open for anyone to view and issue IOUs to and from.
All IOUs are repudiable so access can be tightened as it is deemed necessary.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Account names, like jets:bob, are treated as unique identifiers in Yootles--there are no account numbers.
This means the process of renaming an account is convoluted, but the interface can mask this.
Here we describe the underlying process.
Suppose Bob has an account old:bob he wants to rename to or merge in with new:bob.
If old:bob's balances are all zero and Bob has root access he can revoke everyone else's access to old:bob and then simply abandon it.
There is no way to actually delete it--this is because entries with old:bob may still exist in people's transaction histories.
If old:bob has one or more nonzero balances, renaming consists of transferring those balances to new:bob.
Say Alice owes old:bob 8 yootles.
By issuing an IOU from old:bob to Alice for 8 yootles we zero the
Alice balance
Then we issue an IOU of 8 yootles from Alice to new:bob.
Now Alice owes new:bob instead of old:bob.
We repeat this until all old:bob's balances are zero, and then (optionally) revoke access to old:bob.
(9)
The above process can't be entirely hidden from the end users.
At the least, users who have balances with old:bob will see two new IOUs, one zeroing out with old:bob and one creating the balance with new:bob.
This serves as a natural way to inform all those concerned about account name changes.
But from Bob's perspective, the above process can be automated and the
API provides a command (see section merge (merging/renaming accounts)) encapsulating the above steps of
zeroing out balances in an old account and transferring them to a new
account (note that nothing prevents the new account from already
existing and having other balances).
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
The fundamental data structures for accounts, account groups, IOUs, interest, credit, and currencies are given below. These are followed by data structures for users (people who can log in to the Yootles system), aliases (e.g., Facebook ID or email address) and access control (who can do what with which accounts). This is a subset of the underlying database schema. Data types are given in parentheses. Timestamps are integers specifying the number of seconds since 1970-01-01 00:00:00 GMT, i.e., unixtime. In the database, pointers are realized as integer IDs.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
group An account group name is prefixed to an account name, creating a
namespace for a related set of accounts.
The fields for the group data structure are group name and group
description:
name (string).
Name of the account group, e.g., "jets" or "reevesfamily".
desc (string).
Text field describing the group.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
account An account (not to be confused with a user) represents an entity that can issue and receive IOUs, extend or accept credit, etc.
grp (pointer to group).
The group this account belongs to.
name (string).
The account name, e.g., "alice", "bob", or "mom".
Note that the combination of grp and name must be globally unique and cannot be changed.
desc (string).
Text field describing this account.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
currency Currencies can include yootles, real money, time, even non-commodity items such as books (if you wanted to keep track of books you lent to friends).
code (string).
Symbol for the currency.
Initial values: {ytl, usd, inr, can, beer}.
name (string).
Name of the currency.
Initial values: {Yootles, US Dollars, Indian Rupees, Canadian Dollars, Beers}.
desc (string).
Text field describing the currency.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
rawIOU Raw IOUs, which may encompass multilateral IOUs and repeating IOUs, are
stored in a way that corresponds directly to what prompted issuing the
IOU (see section owe (adding or modifying an IOU)).
amt (string).
The amount of the IOU, stored as a mathematical expression matching /[\d\.\+\-\/\*\ \(\)]+/ and evaluating to a number.
from (string).
(10)
The account(s) issuing the IOU, stored as a mathematical expression where the symbols are account names.
to (string).
(11)
The account(s) receiving the IOU, same format as from.
The accounts in from and to can be prefixed with the account name (e.g., "yooniversal:alice") but if not, use the group specified by grp.
when (timestamp).
(12)
Date/time of the IOU (need not be when it was actually issued).
(13)
why (string).
Text field giving a short reason for the IOU.
rpt (real).
How often the IOU should automatically repeat (the period, not the frequency).
Default: -1, meaning don't repeat.
rptunit ({day, week, month, year}).
The unit of measure for rpt.
E.g., rpt=1/2 and rptunit=year means repeat twice a year (semiannually).
til (timestamp).
Time after which no auto-repeats (if the last repeated IOU falls before this time then prorate the last IOU). Default: -1, meaning forever.
cur (pointer to currency).
Currency. Defaults to yootles.(14)
grp (pointer to group).
The account group that the issuer and recipient accounts are part of, if they don't specify groups explicitly.
This allows accounts to be specified as just name instead of group:name.
Default: "yooniversal".
replaces (pointer to rawIOU).
The IOU that this IOU replaces.
Default: -1, meaning it doesn't replace anything.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
atomicIOU Atomic IOUs are the raw IOUs expanded out into bilateral, non-repeating IOUs. Atomic IOUs are also created to keep track of interest payments. All atomic IOUs can be reconstructed from the raw IOUs and the interest rates.
amt (real). The amount of the atomic IOU.
from (pointer to account).
(15)
The account issuing the IOU.
to (pointer to account).
(16)
The account receiving the IOU.
when (timestamp).
(17)
Date/time of the atomic IOU.
raw (pointer to rawIOU). The raw IOU which corresponds to this atomic IOU.
Automatically generated interest IOUs have this field set to null.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
intRate This data structure is used to specify an interest rate for a pair of
accounts at a given time.
Note that interest rates are undirected, so, e.g., alice/bob and bob/alice cannot have different interest rates.
acct1 (pointer to account). The first account.
acct2 (pointer to account). The second account.
rate (real).
The interest rate, stored as a fractional annual rate, not a percent, e.g., 0.05 for 5%.
date (timestamp).
When this interest rate goes into effect (interest rates apply until a new entry begins).
Note we allow the setting of a whole historical interest timetable, not just changing the interest rate over time.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
credit This data structure is used to specify credit limits (or "trust") between accounts. Credit limits are directed, i.e., they need not be symmetric for a pair of accounts.
issuer (pointer to account). The account issuing the credit.
recip (pointer to account). The account receiving the credit.
amt (real). The credit limit.
cur (pointer to currency). The currency for this line of credit.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
All accounting is done in terms of the data structures in the previous section. There is a strict conceptual separation between users and accounts. Following are data structures pertaining to users.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
user This data structure stores usernames and passwords for users.
username (string).
Username with which the user accesses the Yootles system.
Note that there need be no connection between this name and the names of accounts the user can access.
passwd (string). The user's password, stored as an md5 hash of password plus salt.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
aliastype An alias type is an attribute of a user specifying another identifier (besides username) for that user. For example, "real name" or "email address" are alias types.
code (string).
Symbol for this alias type; one of
{username, realname, email, yahoo, aol, msn, phone, 4info, icq, google, facebook}.
name (string). Name for this type, e.g., "Yahoo Messenger".
desc (string). Description.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
alias The alias data structure gives an attribute/value pair for a user
(where the attribute is given by aliastype) in order to specify an
alternate identifier for the user, such as Facebook ID or email address.
(18)
user (pointer to user). The user.
type (pointer to aliastype). The alias type.
alias (string).
The actual alias of the specified type, e.g., "bob@bob.com", "12345", "Bob Smith", or "bobmeister12".
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
access The access data structure encodes the ways in which a given
user is allowed to access a given account.
user (pointer to user). The user.
acct (pointer to account). The account.
root (boolean). Whether this user can change these flags for other users.
view (boolean). Whether this user can view all the IOUs for this account.
ctrl (boolean). Whether this user can issue IOUs from this account.
This defaults to true for all users and all accounts.
main (boolean).
Whether this is the main account for this user (note that main
implies mine).
(19)
mine (real).
The fraction of this account's net balance that should be included in this user's net balance; typically 0 or 1.
ntfy (boolean). Whether this user should get notified about IOUs for this account.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Yootles API calls are made either by a specific Yootles user who specifies their Yootles username and (hashed, salted) password with each call, or on behalf of a specific Yootles user by a trusted application that provides an application-specific user alias and a (hashed, salted) application key with every call.
In both cases, API calls are REST-style over HTTP. Contrary to true REST, the HTTP status code is always set to 200 (success) with any application-level errors indicated in the API output (see section API Output). (20) Standard URI encoding is used in the URLs. Spaces may be mapped either to "+" or "%20". Alphanumeric characters as well as certain characters such as ":" and "_" need not be escaped. The character "+", of course, must be escaped since otherwise it will be decoded as a space.
In the current beta period the API can be accessed at the following URL instead of yootles.com:
http://is002.yrl.re4.yahoo.com
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
The following is a template for an API command when the caller knows the Yootles username (we call this the invoking user) and password:
http://yootles.com/api?cmd=COMMAND
&invoker=USERNAME
×tamp=TIMESTAMP
&key=KEY
with additional attr=val pairs making up the arguments for the command.
For example, here is an example call to issue a simple IOU from Alice to
Bob (see section owe (adding or modifying an IOU)):
http://yootles.com/api?cmd=owe&amt=5&from=alice:ac&to=bob:b&why=lunch
&invoker=alice
×tamp=1179548280
&key=289fe8ab2b3c2c19
In the next chapter we present API calls in an abstracted form. For example, we write the above as
alice> owe(amt=5, from=alice:ac, to=bob:b, why=lunch)
or more generally, as
USERNAME> COMMAND(attr=val, attr2=val2, ...)
This indicates that COMMAND is being called with the given named parameters and that the call is being invoked by USERNAME.
The additional fields (key and timestamp, as well as app, described in the next subsection) are left implicit in the abstracted form.
The timestamp field gives the unixtime when the call was issued and the key field must be calculated as
KEY = md5(USERNAME + PASSWD + TIMESTAMP)
where PASSWD is the user's password (and `+' is string concatenation).
By hashing the timestamp, and not allowing old timestamps, the system
limits replay attacks.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
The above suffices for applications that know usernames and passwords. Any user can, for example, use the API with calls like the above to interact with their own accounts and IOUs programmatically. We also allow access by trusted applications that do not (and cannot) know individual users' Yootles passwords but are nonetheless allowed to perform actions on behalf of users. The Yootles system maintains a mapping from identifiers on various services (including Facebook IDs, Yahoo Messenger and AIM screen names, email addresses, phone numbers for SMS, etc.) so that a trusted application need only provide the username or other identifier for its own service. For example, on Facebook if you have the numerical Facebook ID for a user, make the API call as follows:
http://yootles.com/api?cmd=COMMAND
&invoker=facebook:FACEBOOKID
&app=APPNAME
×tamp=TIMESTAMP
&key=KEY
Anyone can apply for an application name (APPNAME) and key (APPKEY) by emailing api@yootles.com.
The key field above is then calculated as
KEY = md5(FACEBOOKID + APPKEY + TIMESTAMP).
Since the application uses Facebook's authentication and is trusted, the Yootles password for the user on whose behalf the call is being made need not be provided.
Note that the Facebook application must first create a Yootles user (see section addusr (creating new users)) or associate the Facebook ID with an existing Yootles user (see section alias (creating, updating, and querying user aliases)).
It may use any convention it chooses (e.g., FirstnameLastname); the
addusr command indicates availability of usernames.
Users can later change their usernames (see section usr (updating and querying users)), but a user using
the Facebook application exclusively need never even know what their
Yootles username is.
(21)
Other applications are analogous to the Facebook example. For example, the messenger bot application makes API calls of the form
http://yootles.com/api?cmd=COMMAND
&invoker=yahoo:YAHOOID
&app=yimbot
×tamp=TIMESTAMP
&key=KEY
with KEY = md5(YAHOOID + APPKEY + TIMESTAMP).
Besides Facebook and Yahoo Messenger, there are additional invoker
types for other interfaces including an email bot ("email") and an SMS bot ("4info").
Finally, we allow the API to be called with the same consistent syntax even for calls by users instead of applications. Namely,
http://yootles.com/api?cmd=COMMAND
&invoker=username:USERNAME
&app=raw
×tamp=TIMESTAMP
&key=KEY
and in fact the slightly simpler syntax given in the previous section is syntactic sugar for the above more general format.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
If you specify an additional field, output,
you can choose the output format from the set
{xml, json, perl, lisp, mma, php}
with xml the default.
(22)
These and other output formats are described in the next subsection.
All API calls return a set of labeled values, including a status code and a human-readable message. Formatted as JSON, API output will look like this:
{ "status" : CODE,
"message" : MESSAGE,
... additional "attr":"val" pairs ... }
CODE is one of the following:
MESSAGE is some human-readable output for the command (useful for command line interfaces such as the messenger bot, and also providing an explanation in case of error).
After that come additional attribute/value pairs specific to the individual command, if any.
Arguments for commands and their output are detailed in the next chapter.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Following is a hypothetical response that captures all the structure of the various Yootles API responses, translated into several formats. All except YAML are currently supported. (23)
{ "status" : 200,
"message" : "hello world",
"foo" : [{"a":1, "b":2}, {"a":10, "b":20}],
"bar" : {"x":"one", "y":"two", "z":"three"} }
The following perl expression can be directly assigned to a hashref variable. The perl module Data::Dumper generates data in the following form, given a hashref.
{ "status" => 200,
"message" => "hello world",
"foo" => [{"a"=>1, "b"=>2}, {"a"=>10, "b"=>20}],
"bar" => {"x"=>"one", "y"=>"two", "z"=>"three"} }
Using the perl module XML::Simple with options NoAttr and rootname
"ytl", the following XML corresponds one-to-one with the perl expression
above.
Note that the XML output cannot distinguish between a list containing
just one element and the element itself.
Also, if a list is empty then the XML output will not include the tag
for that list at all.
<ytl> <status>200</status> <message>hello world</message> <foo> <a>1</a> <b>2</b> </foo> <foo> <a>10</a> <b>20</b> </foo> <bar> <x>one</x> <y>two</y> <z>three</z> </bar> </ytl>
{ "status" -> 200,
"message" -> "hello world",
"foo" -> {{"a"->1, "b"->2}, {"a"->10, "b"->20}},
"bar" -> {"x"->"one", "y"->"two", "z"->"three"} }
( ("status" . 200)
("message" . "hello world")
("foo" . ((("a" . 1) ("b" . 2)) (("a" . 10) ("b" . 20))))
("bar" . (("x" . "one") ("y" . "two") ("z" . "three"))) )
array('status' => 200,
'message' => "hello world",
'foo' => array(array('a'=>1, 'b'=>2), array('a'=>10, 'b'=>20)),
'bar' => array('x'=>"one", 'y'=>"two", 'z'=>"three") )
---!ytl
status: 200
message: hello world
foo:
- a: 1
b: 2
- a: 10
b: 2
bar:
x: one
y: two
z: three
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
As described above, users and accounts are treated distinctly by the
Yootles system.
Users can access any number of accounts and an account can be accessed
by any number of users.
Each user, however, has at most one main account and in some
applications it is convenient to refer to accounts by the primary user.
Additionally, it is often convenient to refer to users by alias instead
of username.
The Yootles system supports macro expansion in API calls to facilitate
this and avoid the need for multiple lookup calls.
Specifically, it allows you to specify users by their
aliases (such as Facebook ID or Yahoo ID) as well
as refer to a user's main account indirectly by user.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
Similar to how accounts are specified by group:name, users
may be specified by, for example, yahoo:alice75 or
email:alice@alice.com or facebook:24601.
Anywhere a username can be specified, an alias can be substituted.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
The special syntax [t:a] or [u] expands to the main
account of user u or the user referenced by alias t:a.
This syntax can be used wherever an account is required.
An error is returned if the specified user has no main account.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
$INVOKER is special syntax that expands to the actual username
of the invoking user.
This can be used anywhere.
For example, set grp=$INVOKER to specify the invoker's username as
the account group or use [$INVOKER] to refer to the main account
of the invoking user, per the bracket syntax above.
(This is equivalent to [t:a] where t:a is an alias for the invoking user.)
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
usr (updating and querying users) The usr command allows changing usernames and passwords
and looking up users by their aliases, such as email address or Facebook ID.
Other commands allow the creation of new users (see section addusr (creating new users)) and
adding or changing aliases for users (see section alias (creating, updating, and querying user aliases)).
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
usr()
returns the invoking user's username.
usr(username=u)
changes the Yootles username of the invoking user to u, returning the previous username.
usr(passwd=p)
changes the password of the invoking user to p, returning the previous password. (Note that
passwdcannot be specified by applications, only users.)
usr(username=u, passwd=p)
changes both the username and password, returning the previous values.
usr(alias=t:a)
returns the username that has the given alias, t:a, where t is one of the following alias types: (24) {realname, email, yahoo, aol, msn, phone, 4info, icq, google, facebook}.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
If the application issuing the command knows the user's password, the call is made, for example, as follows:
http://yootles.com/api?cmd=usr&username=madeleine
&invoker=valjean
×tamp=1234567890
&key=3c7e8cc4b89de301
Otherwise, for a trusted application (e.g., the Yootles Facebook application which can make calls on behalf of users) that has an application key, a call like the following is made:
http://yootles.com/api?cmd=usr&username=madeleine
&invoker=facebook:24601
&app=facebook
×tamp=123890
&key=fa3de669ef90ca6
Either of the above are expressed in abstracted form (see section The Yootles API) as:
valjean> usr(username=madeleine)
The following is an example response to the above:
{ "status" : 200,
"message" : "Your username has been changed from valjean to madeleine.",
"username" : "valjean" }
alice> usr(passwd=abc123)
{ "status" : 200,
"message" : "Your password has been changed.",
"passwd" : "changeme" }
If both username and password are specified, both previous values will be included in the output.
alice> usr(alias=email:bob77@yahoo.com)
{ "status" : 200,
"message" : "Yootles user bob has email address bob77@yahoo.com.",
"username" : "bob" }
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
addusr (creating new users) Any user can call addusr to create a new user.
An application creating a new user will also need to call the
alias command (see section alias (creating, updating, and querying user aliases)) to create an alias for the new user
and the acct command (see section acct (querying and setting access control)) to associate an account with
the new user.
The account may be an existing account (such as an account created by a
friend) or a new account, which can be created with the
owe command (see section owe (adding or modifying an IOU)).
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
addusr(username=u)
creates a new user with username u and system-generated password. If the user u already exists, the command will return with status code 402.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
alice> addusr(username=carol)
{ "status" : 200,
"message" : "User carol has been created. Use the `request' command
to retrieve the password." }
Or, if the username "carol" is taken, the following response is generated:
{ "status" : 402,
"message" : "Error: User carol already exists." }
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
reg (macro for registering/bootstrapping a new user into the system) The reg command is a macro implemented in terms of the other
Yootles API commands.
This is a placeholder for more complete documentation of this command.
# Create a new user, associate the alias, create the main account, and
# link the main account.
# Eg, alias = facebook:24601, username = firstname_lastname,
# account = firstname_lastname:firstname
# force defaults to 0; if force=1 then g:ac can't be specified
reg(username=u, alias=t:al, [force=f], [acct=g:ac])
if f==0 (0 is the default for f)
addusr(username=u) and abort if this fails
else
do addusr(username=u) with variations on u until it succeeds
let u = the username of the added user
let g:ac = u:u if acct not specified.
alias(alias=t:al) # NB: invoked with invoker=username:u
owe(amt=0.001, from=g:ac, to=yooniversal:admin, why=new_user_fee, cur=ytl)
acct(user=u, acct=g:ac, main=1, ntfy=1)
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
alias (creating, updating, and querying user aliases) The Yootles system stores for each user additional aliases such as email addresses
and instant messenger screen names.
The alias command allows a user to query and update their aliases, and to add new ones.
Other commands allow looking up a user by alias (see section usr (updating and querying users)) and managing accounts for users (see section acct (querying and setting access control)).
The following parameters can be specified:
aliastype - The alias type. E.g., "email".
alias - The alias, with alias type. E.g., "email:alice@alice.com".
aname - The descriptive name of the alias type, typically omitted.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
alias()
returns a hash table mapping alias types to aliases for the invoking user. See example 1 below.
alias(aliastype=t)
returns the alias of type t, along with the descriptive name of the alias type. If no such alias type exists, the empty string is returned.
alias(alias=t:a, [aname=n])
adds or updates alias type t to alias a. It returns the previous values, or the empty string if t did not already exist. If
anameis also specified, this gives the descriptive name for the alias type (replacing the old one if it exists). See example 4 below.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
alice> alias()
{ "status" : 200,
"message" : "Your aliases: email = alice@alice.com,
realname = Alice L. Canonikopolous,
facebook = 24601, yahoo = alice84",
"aliases" : {"username":"alice", "email":"alice@alice.ccom",
"realname":"Alice L. Canonikopolous", "facebook":24601,
"yahoo":"alice84"} }
alice> alias(aliastype=yahoo)
{ "status" : 200,
"message" : "Your Yahoo Messenger alias is alice84.",
"alias" : "alice84",
"aname" : "Yahoo Messenger ID" }
Note that this is also the way to query the descriptive name
(aname) for the specified alias type.
This works exactly like in the previous example--"username" is treated
like any other alias type.
(Users, as opposed to trusted applications, will never need this since a
user couldn't have issued the alias command without knowing
their username.)
Note that this usage of the alias command has the same
functionality as calling usr with no arguments (see section usr (updating and querying users)).
alice/24601> alias(aliastype=username)
{ "status" : 200,
"message" : "Your Yootles username is alice.",
"alias" : "alice",
"aname" : "Yootles username" }
alice> alias(alias=openid:oid.org/alice, aname="Open ID")
{ "status" : 200,
"message" : "New alias added: Open ID (openid) = oid.org/alice",
"aliastype" : "",
"alias" : "",
"aname" : "" }
The output fields aliastype, aname, and
alias give the previous values, or the empty string if this is
a new alias.
alice> alias(alias=facebook:24602)
{ "status" : 200,
"message" : "Facebook ID changed from 24601 to 24602.",
"aliastype" : "facebook",
"alias" : 24601,
"aname" : "Facebook ID" }
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
request (email a user their username and password) When an application creates a new user, the Yootles system generates a
random password for that user, which the application never sees (since
applications have application keys and can issue commands on behalf of
users without knowing user passwords).
A user using a particular application need never know their Yootles
username or password, but if the user would like to use some other
application that uses the Yootles API, the user will need to provide
their username and password to that other application.
The request command causes an email to be sent to the user with
their username and password.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
request()
returns success and, as a side effect, generates email. If email could not be sent--for example, because no email alias is set (see section
alias(creating, updating, and querying user aliases))--it returns status code 402.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
alice> request()
{ "status" : 200,
"message" : "Your Yootles username and password has been
sent to alice@alice.com" }
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
There is only one "bug": this command has not been implemented yet.
For the current alpha version, the password is just always set to
changeme and if you actually change it and then forget it, you'll have
to email help@yootles.com.
Note that trusted applications, like the Facebook application, do not
need to deal with user passwords.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
acct (querying and setting access control) The acct command allows management of the access
data structure (see section Data Structures Related to Users).
That is, it allows the setting and querying of which users have access
to which accounts.
Parameters for the acct command correspond directly to the fields in the
access data structure, namely:
user, acct, main, mine, ntfy, root.
| [ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] |
acct([user=u])
returns, for user u (default: the invoking user), the
mainaccount, a list ofmineaccounts (accounts for which u hasmine> 0), a list ofntfyaccounts (accounts for which u gets notifications), and a list ofrootaccounts (accounts for which u has root access).
acct(user=u, acct=a)
returns, for each access field, user u's access to account a.
acct(acct=a)
returns, for each access field, the list of users (possibly empty) who have the corresponding access to account a. Having
mine>0 suffices to be included in the list of users havingmineaccess.
acct([user=u], acct