Amatino API v0.0.23

A new version of the Amatino API is serving requests at api.amatino.io. v0.0.23 is mostly backwards compatible with v0.0.22.

  • Responses from /accounts will provide informative error messages when requests yield 400. In the past, such requests would only provide a generic “400 - Bad Request” response, making debugging difficult. You can now expect precise errors, such as “Missing "name" key” or “"description" value too long – max length 4096 characters.”
  • Fixed a bug wherein Accounts could sometimes be returned with missing keys
  • Account deletion requests now return data consistent with documentation. They previously returned an undocumented data structure
  • Account update requests previously demanded a unit key. This was inconsistent with global_unit_id demanded elsewhere, and with documentation. Update requests now demand the global_unit_id key as documented.
  • Nullable Account request fields (e.g. parent_account_id) are now optional. That is, you no longer need to include the key with a null value. You may continue to do so.
  • Eliminated PATCH support for /accounts. The method was seldom used, and offered little value given Transaction data cannot be destroyed through Account deletion.

Amatino API 0.0.19 Released

0.0.19 modifies Entity listing behaviour. It is now possible to search for Entities by name. The manner in which Amatino conveys listed Entities changed, and this new manner will be rolled out across all Amatino objects in time.

0.0.18 code is not backwards compatible with API 0.0.19 with respect to Entity listing.

New Entity Listing Model

Prior to 0.0.19, a request to /entities/list would return a package of the form:

{
    'entities': [...]
    'number_of_pages': [integer],
    'page': [integer],
    'state': [string],
    'generated_time': [string]
}

… Wherein the Entity objects were returned under the "entities" key. In 0.0.19, Entities are now returned as a list of Entity objects. There is no encapsulating package providing list metadata.

Instead, the Entity object itself is aware of its own disposition with respect to a listing query. Inside the Entity object is a new keyed value, "disposition":

{
    "entity_id": [string],
    "name": [string],
    "owner": [integer],
    "description": [string],
    "region_id": [integer],
    "permissions_graph": [object],
    "disposition": {
        "sequence": [integer],
        "count": [integer],
        "limit": [integer],
        "offset": [integer]
    }
}

The meanings of the four integers inside the Disposition are:

Limit: The maximum number of objects returned in response to the request.

Offset: The sequence number at which the returned objects start.

Count: The total number of objects having attributes that satisfy the request. For example, if you request all Entities whose name starts with "Great", with a limit of 10 and offset 0, and there are 12 entities with such names, you will receive a "count" value of 12 and a "limit" value of 10.

Sequence: The unique position of this object within all possibly returned objects satisfying the request conditions.

In future, this model will be adopted by all Amatino objects that may be listed. That is, they will all include a Disposition object.

Searching by Name

The /entities path now accepts a new URL parameter: name. The value for name can be any string value between 3 and 64 characters long. It is optional.

Amatino will search for all Entities with a case-insensitive name that contains the specified string. For example, "name=orporation" will return Entities with name "Excellent Corporation" and "PROFITABLE CORPORATION LTD".

You can omit the name key entirely.

0.0.19 Compatible Libraries

New versions of Amatino client libraries are available, compatible with API 0.0.19:

Changelogs

Amatino Python 0.0.16

  • Added Entity.retrieve_list() -> List[Entity] method
  • Fixed bad type annotation, Entity.retrieve() now hints at return type of Optional[Entity]
  • Added Disposition class
  • Added Entity.disposition property
  • Remove debug print from Signature computation

Amatino Swift 0.0.14

  • Removed deprecated EntityList
  • Added EntityList.retrieveList(), available in both error-first and and Result callback forms
  • Added Disposition struct
  • Added Entity.disposition property
  • Removed EntityListScope enum
  • Added State enum

Amatino API v0.0.18 [Heavily Breaking Changes]

A new version of the Amatino API has been deployed. 0.0.18 contains breaking changes – Any code interfacing with versions below 0.0.18 will not work with 0.0.18.

0.0.18 modifies the way HMAC signatures are computed. JSON bodies are no longer included in signature computation. Their inclusion was causing intermittent issues for customers: Seemingly at random, requests would be met with 401 - Not Authenticated responses.

The inclusion of JSON bodies in HMAC computation was, in hindsight, a poor decision. By definition, JSON object key/value ordering is indeterminate. If a key/value pair happens to be parsed in a different order on either side of the client / API connection, the request would fail.

Along with API 0.0.18, the following new client libraries are available:

Each of these libraries supports API 0.0.18.

We never want to make breaking changes like this. We understand how frustrating it is to have integrations break, and apologise profusely. While Amatino is in an “alpha” state (pre v1.0.0), we will make these changes as rarely as possible. After v1.0.0, breaking changes will only be made with prior versions remaining online.

Amatino API v0.0.16 Released

HTTP API update! 0.0.16 is now out in the wild, serving requests. Here’s what it includes:

  • Fixed a bug causing unauthorised and unauthenticated requests to return 500 errors rather than 401 / 403
  • Server header now includes the API version number
  • Fixed a bug causing Region listing to fail in certain unusual circumstances
  • Fixed a bug causing Entity permission records to enter an invalid state when that Entity was updated with a null record
  • Fixed a bug causing arbitrary requests to 500, due to a fault in Amatino’s billing system
  • Added various tests to catch aforementioned failure cases in future

Enjoy! And please continue to report bugs: @AmatinoAPI on Twitter / support@amatino.io.

Amatino API v0.0.8 now live

A new version of the Amatino API is live in all regions. API 0.0.8 may be substantially backward compatible with 0.0.7, however, is not tested as such and relying on backwards compatibility during these 0.0.X releases is not recommended.

  • Fixed a bug which could cause a stale, cached Performance or Position to be returned after a Transaction insertion
  • Fixed a bug causing Custom Unit update requests to fail, returning a 500 code.
  • Account objects now feature a ‘Children’ parameter, which contains integer IDs of all direct child Accounts.
  • Fixed a rare condition wherein internal telemetry recording could fail due to a unique key constraint violation, causing the API to spew 500 errors in response to any request
  • Substantial code-style enhancements in the core API to ease maintainability
  • Improved test coverage over rare edge cases in Account operations

On Prices

The upcoming API 0.0.8 release targets Global Units, Custom Units, and a new Price object that bridges them. It is forcing me to consider some difficult questions.

Amatino’s double-entry data model adopts a ‘quantum mechanics’ approach:  Transactions are the atomic units of all financial information, but they themselves are composed of the more fundamental Entry object, with which we never directly interact. This approach allows financial data to be stored as arbitrary precision numbers in their native denomination.

It also means that the data model does not inherently satisfy the double-entry accounting equation. If external constraints are not applied, it would be possible to store an unbalanced Transaction: The composing Entries may not add up to zero, and the equation would not hold.

Obviously, Amatino is designed with stringent constraints. Attempt to insert an unbalanced Transaction and the digits ‘400’ will present themselves quick smart. But what if a Transaction balance is changed after it is inserted?

How could this happen? It is a matter of relativity. Say you create a Transaction (TX) worth 1 U.S. Dollar (USD) with Entries party to two Accounts: One denominated in USD, and one denominated in Woozlewozzles (WZL). WZL being some Custom Unit you have fed to Amatino.

Perhaps TX is created on 1 Jan 2018, dated at 1 Jan 2018, and at that time, the last WZL / USD price you provided was for 31 Dec 2017, at 2 WZL = 1 USD. Amatino will eat TX and store one Entry for 1 USD, and one Entry for 2 WZL.

Now suppose that on 2 Jan 2018, you have access to new price data. You supply Amatino with a 1 Jan 2018 WZL / USD price of 1 WZL = 1 USD. Ceteris paribus, TX is now unbalanced: Attempt to retrieve TX and Amatino will compute output using the price with time closest-to-but-less-than TX time: 1 Jan 2018.

That yields a WZL entry with double the value that it had at Transaction entry. The double-entry equation is violated and the universe (presumably) implodes.

What’s the solution? The two I am working with right now are:

1. Anchors

When creating a Transaction, a client may specify some (but not all) constituent Entries as being ‘anchors’. When a new Price is inserted, Amatino looks for any Transactions with Entries party to an Account denominated in the Priced unit, with an effective time less than the next most recent Price, and greater-than-or-equal to the new Price.

Amatino then calculates the difference between the old balance and the new balance based on the new Price. That difference is distributed proportionally across any (or the only) Entry that is not marked as being an anchor.

One advantage of Anchors is that they would be compatible with future-dated Transactions with heterogeneous Global Unit denominations.

2. Blind Repair

Similarly to solution (1), Blind Repair would look for any Transactions affected by a new Price. Instead of applying the difference in balance to specially identified Entries, Amatino applies the difference to any Entry in the Priced unit.

One advantage of Blind Repair is that it does not add new complexity to the Transaction model. Anchors would require implicit or explicit decision making on every Transaction entry.

Blind Repair would not be compatible with future-dated Transactions with heterogenous Global Unit denominations. Global Unit prices are updated metronomically at ~1800 UTC each trading day. If a Transaction contained Entries party to say, USD and Euros, how would Blind Repair choose which Entries to adjust? I don’t see a way to make the process deterministic.

What to do?

Right now, I am leaning towards choosing Blind Repair. I would combine it with a new restriction: Transactions cannot be future-dated when they are denominated in heterogenous units where one of those units is a Global Unit.

It will be frustrating to be unable to conveniently future-date known upcoming or recurring Transactions with such denominations. However, I think the increased complexity of Anchors means they are not a viable alternative.

Have thoughts on a third way? Let me know in the comments!

– Hugh

 

API v0.0.5 Released – Ledger Changes

A new version of the Amatino API has been deployed to all regions. 0.0.5 contains breaking changes to the Ledger object, and is not backward compatible with 0.0.4.

  • Ledgers may now only be retrieved one at a time
  • Ledgers are now paginated in rows of 500
  • Ledgers may now be returned in order of ascending or descending transaction time
  • Ledgers now require a order_oldest_first boolean argument
  • Returned Ledger root type is now a JSON Object rather than a JSON Array
  • Returned Ledger start_time and end_time will now be the start and end of the Ledger window, rather than the earliest and latest Transaction in the Ledger
  • Ledger end_time will now default to now at UTC if supplied as null
  • Ledger start_time will now default to end_time – 1 year if supplied as null
  • Fixed a bug that could cause Amatino to return a stale cached Ledger when null had previously been provided as a Ledger start time
  • Fixed a bug that caused Transactions to be stored with incorrect times (The transaction_time field was not being properly interpreted as UTC)

All the above changes also apply identically to the RecursiveLedger object.

The Amatino API obeys the Semantic Versioning convention: Any version before 1.0.0 should be considered unstable, and breaking changes may occur at any time. Even so, I don’t want to cause breaking changes unnecessarily, and don’t take this lightly.

These changes are aimed at making Ledger objects more useful in real world applications. They are the result of experimentation during development of the upcoming Amatino Swift v0.0.5.

– Hugh

API v0.0.2 Released

AKA: Data in HTTP GET request bodies! Fun for the whole family!

A new version of the Amatino API is now live. API 0.0.2 makes the following changes, all of which are backwards compatible with 0.0.1. All regions have been updated to 0.0.2.

  • Added ability to parse JSON arguments from GET URL query string
  • Removed some redundant input validation on Transaction creation

Many Amatino GET actions require JSON arguments. For example, the retrieval of a Transaction requires the supply (or explicit omission) of a denominating currency.

There is substantial debate as to whether data should ever be included in the body of GET requests. Some HTTP libraries will silently strip body data from GET requests. The big dogs say GET body data is a no-no. This hit me in the face while developing Amatino Swift 0.0.2.

Rather than engaging in the debate, the Amatino API now supports both worlds. Where Amatino requires JSON data arguments to refine a request, those arguments may now be supplied via an arguments url parameter, wherein the value supplied is URL-safe Base64 encoded JSON.

The Amatino API’s version of ‘URL-safe Base64’ is the replacement of all `+` characters with `-`, and `/` with `_``=` padding characters should be left in place.

For example, say an Amatino action requires a JSON object specifying key foo with value bar. Where in 0.0.1 the only option was to supply {"foo":"bar"} in the request body, in 0.0.2 we can include arguments=eyJmb28iOiAiYmGyIn0= in the URL query string.

This is riveting stuff, but don’t worry, Amatino’s client libraries will take care of all of it for you!