Warning! Construction underway! Amatino is in an 'alpha' state. Not all features are operational. See roadmap

Getting started

Amatino provides double-entry accounting as a service. To use our service, you make HTTP requests over the internet. A request might say 'create a new account', 'retrieve a ledger', 'delete a transaction', and so on.

You can make requests from practically any machine you like. A machine might be your laptop, phone, or a server on which you run an application of your own.

All the different requests you can make, including their requirements, outputs, and behaviours are documented in detail in the Amatino API Documentation.

Making requests

You can make HTTP requests using your language of choice. For example, in Python you could use urllib.request, in Swift Foundation.URLSession, or in Node.js the HTTPS module. However, making requests using lower level HTTP libraries can be pretty tedious, which is why Amatino provides open source client libraries in Python, Swift, and JavaScript (Node.js).

These libraries abstract away the nitty-gritty HTTP stuff and present you with objects like Accounts and Entities. However, in Amatino's current Alpha state, these libraries are incomplete. They do not yet cover the full range of Amatino API capabilities accessible with raw HTTP requests.

If the story ended there, Amatino would kind of suck. No one wants to have to form raw HTTP requests to use an Alpha product. So, while the client libraries are being built up, we have provided an interim solution: The AmatinoAlpha object.

The AmatinoAlpha object is created like so:

from amatino import AmatinoAlpha
AMATINO_ALPHA = AmatinoAlpha( email="clever@cookie.com", secret="high entropy passphrase" )
import Amatino
let _ = AmatinoAlpha.create( email: "clever@cookie.com", secret: "high entropy passphrase" callback: {(error, amatinoAlpha) in //Do stuff with amatinoAlpha })
const Amatino = require('amatino');
let _ = new Amatino.AmatinoAlpha( 'clever@cookie.com', 'high entropy passphrase', (error, amatinoAlpha) => { // We'll use `amatinoAlpha` in the next example } );

You can make requests of all Amatino API resources using the AmatinoAlpha object. The syntax is not as expressive as the complete client libraries aim to be. It is very thin wrapper around raw HTTP requests. Its only objective is to make Alpha requests slightly more bearable while the Amatino client libraries are under construction.

Before we dive into making requests, let's take a short detour via the security protocols that Amatino enforces. Having knowledge of Amatino's security expectations will make the coming usage examples more understandable.

Security protocols

To to get to Amatino, your requests must travel through the dangerous, barren wasteland known as the Internet. Bandits roam the internet. Before your requests ride into the danger zone, Amatino equips them with impenetrable encryption shields.

In technical terms, all requests to Amatino are encrypted under the Transport Layer Security (TLS) 1.2 standard, and authenticated with a Secure Hash Algorithm 2 Hashed Message Authentication Code (SHA-2 HMAC).

Any request not shielded by TLS 1.2 will be ejected into the void. Amatino won't allow you to initiate a connection with our servers without TLS 1.2 protection. In the jargon, it will be 'dropped' at our network boundary and you won't receive a response.

Any request that does not include a valid SHA-2 HMAC will be stopped and frisked. There is only one case where it will be allowed to proceed: That wherein you are trying to create a Session (we'll get to Sessions later on). Any other request without an SHA-2 HMAC will receive a Not Authorised response, with HTTP code 401.

Anatomy of a request

A request to Amatino has five components. Most often, you will use them all together. You won't generally have to worry about each component if you are using a client library. However, in Amatino's Alpha stage, those libraries are incomplete, and you are likely using the AmatinoAlpha object.

Because the AmatinoAlpha object is quite bare-bones, it helps to have an understanding of the five request components.

  1. Path: A request to Amatino is sent to a URL. For example: "https://api.amatino.io/transactions". The path is the bit of the URL following "amatino.io". In this case, it is "/transactions". Think of the path as being the name of the resource you which to act on. In this case, you may wish to create a Transaction.
  2. Query string: The bit of the URL following the path. For example, in "https://api.amatino.io/entities?entity_id=ABCD" the query String is "?entity_id=ABCD". The query string is often used to aim retrieval (GET) requests at specific resources.
  3. Method: The verb describing the action you wish to take. For example, 'GET' for retrieval, 'DELETE' for deletion. Amatino uses RFC 2616 standard HTTP methods.
  4. Headers: Where you present your boarding pass. Amatino looks for an SHA-2 HMAC in the headers.
  5. Body: The meat and potatoes of most requests. The body is a JSON string encoded in utf-8. The body might include bulk data such as a set of Transactions you wish to store.

Alright, let's put all that together and store some data!

Creating an Entity

Amatino defines Entities as things that we wish to describe with accounting information. Entities might be people, companies, projects, models, or anything else you like. The first step to storing accounting information in Amatino is to create an Entity.

The Entity documentation describes a 'Create' action for Entity objects, requiring a JSON string name and optional description and region_id parameters. We can create an Entity using the AmatinoAlpha object we created earlier.

entity = AMATINO_ALPHA.request( path="/entities" query_string=None method="POST" body=[{ "name": "My First Entity", "description": None, "region_id": None, }] )
let body = EntityCreateArguments(name: "My First Entity")
let _ = amatinoAlpha.request( path: '/entities', method: .POST queryString: nil> body: [body] callback: {(error, responseData) in // Do stuff with responseData })
let _ = amatinoAlpha.request( '/entities, 'POST, null, [{ 'name': 'My First Entity, 'description': null, 'region_id': null }], (error, responseData) => { const entity = responseData[0] // We'll use `entity` in the next example } );

Behind the scenes, the AmatinoAlpha object handles all the HTTP request mumbo-jumbo, and generates the the necessary SHA-2 HMAC. Otherwise, it is very bare-bones. We're racing to deliver the more expressive Amatino client libraries as quickly as possible.

Per the Entity documentation object section, the returned Entity object contains an entity_id parameter. We can use that parameter to start building an accounts tree.

Creating Accounts

An Account is some collection of economic activity. Examples include a bank account, income from a particular client, dividends from shares, or a credit card.

Amatino's Account documentation lists a 'Create' action for an Account. Like the Entity we created earlier, it requires some data describing the Account. It also requires a query string: The entity_id of the Entity we just created. Here goes:

accounts = AMATINO_ALPHA.request( path="/accounts" query_string="?entity_id=" + entity[0]["entity_id"], method="POST", body=[{ "name": "Subscription income", "type": 4, "parent_account_id": None, "global_unit_id": 5, "custom_unit_id": None, "counterparty_entity_id": None, "description": "Sweet loot", "colour": None }, { "name": "Cash", "type": 1, "parent_account_id": None, "global_unit_id": 5, "custom_unit_id": None, "counterparty_entity_id": None, "description": "Stacks of Benjamins", "colour": None }] )
let cashAccount = try AccountCreateArguments( name: "Cash", type: .asset, description: "Stacks of Benjamins", globalUnit: USD, ) let incomeAccount = try AccountCreateArguments( name: "Sweet loot", type: .income, description: "Subscription income", globalUnit: USD, ) let _ = try amatinoAlpha.request( path: "/accounts", method: .POST queryString: ("?entity_id=" + entity.id) body: [cashAccount, incomeAccount] callback: { (error, responseData) in // Do stuff with responseData })
let _ = amatinoAlpha.request( '/accounts', 'POST', ?entity_id= + entity['entity_id'], [{ 'name': 'Subscription income', 'type': 4, 'parent_account_id': null, 'global_unit_id': 5, 'custom_unit_id': null, 'counterparty_entity_id': null, 'description': 'Sweet loot', 'colour': null }, { 'name': 'Cash', 'type': 1, 'parent_account_id': null, 'global_unit_id': 5, 'custom_unit_id': null, 'counterparty_entity_id': null, 'description': 'Stacks of Benjamins', 'colour': null }], (error, responseData) => { // We'll use these two accounts in the next example const incomeAccount = responseData[0] const cashAccount = responseData[1] } );

Whoah, let's unpack what just happened. Most obviously, we created two accounts, not just one. Most Amatino objects will allow you to create up to ten of them at once. We created two accounts because Amatino is a double-entry accounting system: Without more than one account, we can't have much fun.

We also pulled a number out of thin air: The global_unit_id. That happens to be the id for United States Dollars. You can list all available Global Unit denominations by retrieving a Global Units List.

Such lists of constants are available for many Amatino API objects. For example, we used "income" and "asset" strings to define the "type" of the new accounts. These constants, of which there are five, are available by requesting the Types list.

You might create all sorts of Accounts inside an Entity. Accounts can be nested into large parent-child hierarchies, with heterogenous denominations. Of course, Accounts aren't much use if they are empty, so let's record some Transactions.

Creating Transactions

A Transaction is an exchange of value between two or more Accounts. For example, a receipt of revenue, a raising of an invoice, recording an unrealised gain, or writing off a bad debt. The words 'debit' and 'credit' are synonymous with double-entry accounting, and when working with Transactions those words will finally come out to play.

The Transaction creation documentation specifies a bunch of data, including the account id's of the accounts we wish to debit or credit. We can get those id's from the accounts we just created.

transactions = AMATINO_ALPHA.request( path="/transactions" query_string="?entity_id=" + entity[0]["entity_id"], method="POST", body=[{ "transaction_time": "2017-01-17_17:22:16.51245", "description": "Receipt of some dosh", "global_unit_denomination": 5, "custom_unit_denomination": None, "entries": [{ "account_id": accounts[0]["account_id"], "description": '', "side": 1, "amount": "42.01", }, { "account_id": accounts[1]["account_id"], "description": '', "side": 0, "amount": "42.01", }] }] )
let incomeReceipt = try TransactionCreateArguments( transactionTime: Date(), description: "Receipt of some dosh" globalUnit: USD, entries: [ Entry( side: .debit, account: cashAccount, amount: Decimal(42.02) ), Entry( side: .credit, account: incomeAccount, amount: Decimal(42.02) ) ] ) let _ = amatinoAlpha.request( path: "/transactions", method: .POST queryString: ("?entity_id=" + entity.id) body: [incomeReceipt] callback: { (error, responseData) in // Do stuff with responseData })
let _ = amatinoAlpha.request( '/transactions', 'POST', ?entity_id= + entity['entity_id'], [{ 'transaction_time': '2017-01-17_17:22:16.51245', 'description': 'receipt of some dosh', 'parent_account_id': null, 'global_unit_denomination': 5, 'custom_unit_denomination': null, 'entries': [{ 'account_id': Number(incomeAccount['account_id']), 'description': null, 'side': 1, 'amount': '42.01' }, { 'account_id': Number(cashAccount['account_id']), 'description': null, 'side': 0, 'amount': '42.01' }] }], (error, responseData) => { // Do stuff with the new transaction const doshReceipt = responseData[0] } );

Sneaky: To create a Transaction, we also had to refer to documentation of the Entry object. Entries are individual debits and credits, and Transactions can contain up to one hundred of them. They snuck into the Transaction data under the "entries" key.

The key recurring theme in all these examples is that the AmatinoAlpha object can be fed the path, method, query string and data requirements described in any object's documentation page. In future, client libraries will provide expressive, object oriented abstractions of these raw request elements. For now, we hope the AmatinoAlpha object will provide enough interim capability for you to be able to start experimenting with Amatino.

Did we mention the Trees?

With that, we have created an entity and populated it with accounting data. You can retrieve those data in myriad ways. Ledgers provide lists of transactions from the perspective of a particular Account, while Trees present every account in the Entity with recursive and non-recursive balances.

You can even mix and match denominating units: Store an account in United States Dollars, post Transactions in Pounds Sterling, and retrieve them in Euros. Amatino will seamlessly convert it all for you, near-instantly.

Take a squizz at the Amatino API documentation to see all the available functionality.

Now, a request...

We're just getting started with Amatino, and it is an absolute genuine thrill that you have made it this far. Please check out the Amatino forums: We would love to hear your feedback about what we're doing right, what we're doing wrong, and what you want see us do in future.