Billy API v2 Documentation

Billy's new API is a JSON-based REST API. Our webapp uses the exact same API - which is your guarantee that the API will always support full functionality, and will generally be nice to work with. We take our own medicine!

If you have any questions or feedback (we love feedback!), please don't hesitate to write us at dev@billy.dk.

Table of Contents

Endpoint

The API is located at https://api.billysbilling.com/v2.

All requests must use SSL.

Authentication

We currently support two authentication methods with the API.

OAuth access tokens

Here's how to obtain an access token for your company:

  • Log into your account on app.billysbilling.com.
  • Go to Settings -> Access tokens.
  • Click Create access token.
  • Enter a descriptive name for your token, so you can easily identify it later. Then hit the Save button.
  • Hover over the newly-generated access token and click the magnifier icon to show it clearly.
  • The token will now be selected in a text field, inside a lightbox.

All you have to do now is put this token in a X-Access-Token header. See Code examples for a complete example.

The tokens you create under Settings -> Access tokens are tied to that company only. If you have multiple companies, you'll need a separate token for each one. Tokens are permanent - the don't expire.

We plan to support a 3-legged OAuth solution eventually.

HTTP basic auth

You can use your normal email and password for authentication HTTP Basic Auth. You can try it right now, if you like. Just navigate to https://api.billysbilling.com/v2/user. Enter your email and password in the browser's credentials form, and you'll see your user as a JSON document.

Conventions

Billy's API is very consistent; we use the same conventions for all resources. Generally speaking, there are 5 ways to interact with a resource: Get one item, list, create, update and delete.

A resource is always accessed through its pluralized name. For example, you can access invoices through /v2/invoices.

In the examples that follow, we'll use the invoices resource - but the exact same pattern applies to all resources.

Getting a single record

When getting a single record, you should use GET /v2/invoices/:id (where :id denotes a dynamic slug, and should be replaced by a real invoice ID). The response will be a JSON document with a root key named after the singular name containing the requested record. Invoice example:

{
      "invoice": {
        "id": "3mIN9EbDEeOg8QgAJ4gM",
        "invoiceNo": 41,
        ...
      }
    }

Listing a resource

Use GET /v2/invoices. The response will be a JSON document with a root key named after the pluralized designation and containing an array of found records. Invoice example:

{
    "invoices": [
      {
        "id": "3mIN9EbDEeOg8QgAJ4gM",
        "invoiceNo": 41,
        ...
      },
      {
        "id": "4LmaTkbDEeOg8QgAJ4to",
        "invoiceNo": 42,
        ...
      }
    ]
  }

Creating a record

Use POST /v2/invoices. The request body should be a JSON document containing a single root key named after the singular name, and should be a hash of record properties. Invoice example:

{
    "invoice": {
      "invoiceNo": 41,
      "entryDate": "2013-11-14",
      ...
    }
  }

See more about the response body in the section Responses to create/update/delete. You can get the ID of the newly created record by getting invoices.0.id in the returned JSON document.

Updating a record

Use PUT /v2/invoices/:id. The request body should be a JSON document containing a single root key named after the singular name, and should be a hash of record properties. The hash does not need to include the record's ID - if it does, though, it must be exactly the same ID you used in the URL. You can't change a record's ID. Invoice example:

{
  "invoice": {
    "contactMessage": 41,
    ...
  }
}

Only properties that you set in the invoice hash will be updated. Properties that are left out will be considered as if they are the same. So if all you need to do is to update the contactMessage property, then you don't need to include any other properties.

The response works the same as with a POST request.

Deleting a record

Use DELETE /v2/invoices/:id. The request should not have a body.

DELETE requests are idempotent, meaning that if you try to DELETE the same record more than once (or delete any other non-existing ID), you will still receive a 200 OK response.

See more about the response body in the section 'Responses to create/update/delete requests'.

Responses to create/update/delete requests

When you make POST, PUT, PATCH or DELETE requests to the API, the response will always include all records that changed because of the request. The record you created/updated will of course be included. Example: When you create an invoiceLine for an existing invoice, the amount field on the invoice will also be changed. So a POST /v2/invoiceLines would return something like:

{
        "invoices": [
          {
            "id": "3mIN9EbDEeOg8QgAJ4gM",
            "amount": 200,
            ...
          }
        ],
        invoiceLines: [
          {
            "id": "cgYxHZWCSfajDIvj9Q8yRQ",
            "invoiceId": "3mIN9EbDEeOg8QgAJ4gM",
            ...
          }
        ]
      }

The IDs of deleted records will be accessible through the meta.deletedRecords property. Example: When requesting DELETE /v2/invoices/1234, the response will be:

{
      "meta": {
        "deletedRecords": {
          "invoices": [
            "1234"
          ]
        }
      }
    }

Note that some PUT requests may delete records, too - for example, when you overwrite a record's has-many relationship using an embedded array of child records.

Relationships

The API is smart about relationships. Many resources have relationships, and we distinguish between two kinds of relationships:

  • Belongs-to: A record of resource A has an ID that points to a record of resource B. Example: invoice records have a contactId property which points to a contact record.
  • Has-many: A record of resource A has zero or more records of resource B that point to it. Example: invoice records have zero or more invoiceLine records that have an invoiceId property, which points back. A has-many relationship always implies a belongs-to relationship on the inverse resource.

Normally, when you GET a resource that has relationships, the belongs-to relationship will be presented as the name of the property, suffixed with Id and containing the ID. If you GET /v2/invoices/123 and it responds with a "contactId": "987", you can get the contact by requesting GET /v2/contacts/987. Has-many relationships won't be in the request, by default.

Requesting sideloaded or embedded records

You can include relationships by adding an include parameter to your GET request. The value of the include parameter should be in the form resource.property:mode[,resource.property:mode,...], where resource is the name of a resource, property is the name of a property of that resource, and mode is either sideload (default if the : part is omitted) or embed. You can include multiple relationships by separating them with ,.

Example: You can load invoices with its invoiceLines embedded, its contacts sideloaded, and the contact's country embedded by doing GET /invoices?include=invoice.lines:embed,invoice.contact:sideload,contact.country:embed. The response will be something like:

{
      "invoices: [
        {
          "id": "invoice1",
          "contactId": "contact1",
          ...
          "lines": [
            {
              "id": "line1",
              ...
            },
            {
              "id": "line2",
              ...
            }
          ]
        }
      ],
      "contacts: [
        {
          "id": "contact1",
          "country": {
            "id": "DK",
            "name": "Denmark",
            ...
          },
          ...
        }
      ]
    }

When sideloading belongs-to relationships, the name of the key is the singular name of the resource, suffixed with Id (like the default behavior), and each sideloaded record can be found in a root key named after the plural name of the belongs-to inverse resource. It's recommended to sideload records instead of embedding, to avoid duplication (each belongs-to record is only included in the response once).

When embedding belongs-to relationships, the name of the key is the singular name of the resource, for example contact - and it contains all the record's properties.

When sideloading has-many relationships, all the child IDs are included as an array of strings in the parent record, in a key named after the relationship's name (not the inverse resource), suffixed with Ids.

When embedding has-many relationships, all the full child records are included as an array of hashes in the parent record, in a key named after the relationship's name (not the inverse resource).

Saving embedded records

Some resources supports saving child records embedded. An example is invoices. A POST /v2/invoices's request body could look like this:

{
      "invoice": {
        "invoiceNo": 41,
        ...
        "lines:" [
          {
            "productId": "...",
            "unitPrice": 100,
            ...
          }
        ]
      }
    }

It's advantageous to embed records when possible, as you get fewer round trips to the API, and everything happens as an atomic transaction (either all or no records are saved).

Paging

You can limit long lists of resources using the paging URL params.

To use pages, you can use page and pageSize. For example, to get the second page containing 20 records per page, you would request GET /v2/invoices?page=2&pageSize=20.

pageSize cannot be greater than 1000 (which is also the default if pageSize is omitted).

Whether your results are truncated/paged can be found in the meta.paging key in the response. It will look something like:

{
      "meta": {
        "paging": {
          "page": 4,
          "pageCount": 9,
          "pageSize": 20,
          "total": 192,
          "firstUrl": "https://api.billysbilling.com/v2/invoices?pageSize=20",
          "previousUrl": "https://api.billysbilling.com/v2/invoices?page=3&pageSize=20",
          "nextUrl": "https://api.billysbilling.com/v2/invoices?page=5&pageSize=20",
          "lastUrl": "https://api.billysbilling.com/v2/invoices?page=9&pageSize=20"
        }
      },
      "invoices": [...]
    }

Filtering

When listing most resources, you can filter the results by various properties.

Sorting

When listing most resources, you can sort the results using the sortProperty and sortDirection URL params. Each resource only allows sorting by specific properties. Those properties are noted in each resource's documentation. The sortDirection must be either ASC (default) or DESC.

Code examples

PHP

<?php
    // Reusable client for sending requests to the Billy API
    class BillyClient {
        private $apiToken;
    
        public function __construct($apiToken) {
            $this->apiToken = $apiToken;
        }
    
        public function request($method, $url, $body = null) {
            try {
                $c = curl_init("https://api.billysbilling.com/v2" . $url);
                curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
                curl_setopt($c, CURLOPT_CUSTOMREQUEST, $method);
    
                // Set headers
                curl_setopt($c, CURLOPT_HTTPHEADER, array(
                    "X-Access-Token: " . $this->apiToken,
                    "Content-Type: application/json"
                ));
    
                if ($body) {
                    // Set body
                    curl_setopt($c, CURLOPT_POSTFIELDS, json_encode($body));
                }
    
                // Execute request
                $res = curl_exec($c);
                $status = curl_getinfo($c, CURLINFO_HTTP_CODE);
                $body = json_decode($res);
    
                if ($status >= 400) {
                    throw new Exception("$method: $url failed with $status - $res");
                }
    
                return $body;
            } catch (Exception $e) {
                print_r($e);
                throw $e;
            }
        }
    }
    
    // Creates a contact. The server replies with a list of contacts and we
    // return the id of the first contact of the list
    function createContact($client, $organizationId) {
        $contact = array(
            'organizationId' => $organizationId,
            'name' => "Ninjas",
            'countryId' => "DK"
        );
        $res = $client->request("POST", "/contacts", array('contact' => $contact));
    
        return $res->contacts[0]->id;
    }
    
    // Creates a product. The server replies with a list of products and we
    // return the id of the first product of the list
    function createProduct($client, $organizationId) {
        $product = array(
            'organizationId' => $organizationId,
            'name' => 'Pens',
            'prices' => [array(
                'unitPrice' => 200,
                'currencyId' => 'DKK'
            )]
        );
        $res = $client->request("POST", "/products", array('product' => $product));
    
        return $res->products[0]->id;
    }
    
    // Creates an invoice, the server replies with a list of invoices and we
    // return the id of the first invoice of the list
    function createInvoice($client, $organizationId, $contactId, $productId) {
        $invoice = array(
            'organizationId' => $organizationId,
            'invoiceNo' => 991,
            'entryDate' => '2013-11-14',
            'contactId' => $contactId,
            'lines' => [array(
                'productId' => $productId,
                'unitPrice' => 200
            )]
        );
        $res = $client->request("POST", "/invoices", array('invoice' => $invoice));
    
        return $res->invoices[0]->id;
    }
    
    // Gets the id of the organization associated with the API token.
    function getOrganizationId($client) {
        $res = $client->request("GET", "/organization");
    
        return $res->organization->id;
    }
    
    function getInvoice($client, $invoiceId) {
        $res = $client->request("GET", "/invoices", $invoiceId);
    
        return $res->invoices[0];
    }
    
    function main() {
        $client = new BillyClient("YOUR ACCESS TOKEN HERE");
    
        $currentOrganizationId = getOrganizationId($client);
        $newContactId = createContact($client, $currentOrganizationId);
        $newProductId = createProduct($client, $currentOrganizationId);
        $newInvoiceId = createInvoice($client, $currentOrganizationId, $newContactId, $newProductId);
        $newlyCreatedInvoice = getInvoice($client, $newInvoiceId);
    
        print_r($newlyCreatedInvoice);
    }
    
    // Run script which creates a new contact, a product and an invoice
    main()

Node.js 8+

First run npm install axios.

const axios = require('axios')
    class BillyClient {
        constructor (apiToken) {
            this.apiToken = apiToken
        }
    
        async request (method, url, body) {
            try {
                const res = await axios({
                    baseURL: 'https://api.billysbilling.com/v2',
                    method,
                    url,
                    headers: {
                        'X-Access-Token': this.apiToken,
                        'Content-Type': 'application/json'
                    },
                    data: body
                })
    
                if (res.status >= 400) {
                    throw new Error(${method}: ${url} failed with ${res.status} - ${res.data})
                }
    
                return res.data
            } catch (e) {
                console.error(e)
                throw e
            }
        }
    }
    
    // Creates a contact. The server replies with a list of contacts and we
    // return the id of the first contact of the list
    async function createContact (client, organizationId) {
        const contact = {
            'organizationId': organizationId,
            'name': 'John',
            'countryId': 'DK'
        }
        const res = await client.request('POST', '/contacts', { contact: contact })
    
        return res.contacts[0].id
    }
    
    // Creates a product. The server replies with a list of products and we
    // return the id of the first product of the list
    async function createProduct (client, organizationId) {
        const product = {
            'organizationId': organizationId,
            'name': 'Ninjas',
            'prices': [{
                'unitPrice': 200,
                'currencyId': 'DKK'
            }]
        }
        const res = await client.request('POST', '/products', { product: product })
    
        return res.products[0].id
    }
    
    // Creates an invoice, the server replies with a list of invoices and we
    // return the id of the first invoice of the list
    async function createInvoice (client, organizationId, contactId, productId) {
        const invoice = {
            'organizationId': organizationId,
            'invoiceNo': 5003,
            'entryDate': '2013-11-14',
            'contactId': contactId,
            'lines': [{
                'productId': productId,
                'unitPrice': 200
            }]
        }
        const res = await client.request('POST', '/invoices', { invoice: invoice })
    
        return res.invoices[0].id
    }
    
    // Gets the id of organization associated with the API token.
    async function getOrganizationId (client) {
        const res = await client.request('GET', '/organization')
    
        return res.organization.id
    }
    
    async function getInvoice (client, invoiceId) {
        const res = await client.request('GET', '/invoices', invoiceId)
    
        return res.invoices[0]
    }
    
    async function main () {
        const client = new BillyClient('0aa10580a9f06be67e864cc93c6da68ab5695340')
    
        const currentOrganizationId = await getOrganizationId(client)
        const newContactId = await createContact(client, currentOrganizationId)
        const newProductId = await createProduct(client, currentOrganizationId)
        const newInvoiceId = await createInvoice(client, currentOrganizationId, newContactId, newProductId)
        const newlyCreatedInvoice = await getInvoice(client, newInvoiceId)
    
        console.log(newlyCreatedInvoice)
    }
    
    // Run script which creates a new contact, a product and an invoice
    main()

Python 3

First run pip3 install requests.

import requests
  # Reusable class for sending requests to the Billy API
  class BillyClient:
      def __init__(self, apiToken):
          self.apiToken = apiToken
      def request(self, method, url, body):
          baseUrl = 'https://api.billysbilling.com/v2'
          try:
              response = {
                  'GET': requests.get(
                      baseUrl + url,
                      headers={'X-Access-Token': self.apiToken}
                  ),
                  'POST': requests.post(
                      baseUrl + url,
                      json=body,
                      headers={'X-Access-Token': self.apiToken}
                  ),
              }[method]
              status_code = response.status_code
              raw_body = response.text
              if status_code >= 400:
                  raise requests.exceptions.RequestException(
                      '{}: {} failed with {:d} - {}'
                      .format(method, url, status_code, raw_body)
                  )
              return response.json()
          except requests.exceptions.RequestException as e:
              print(e)
              raise e
  # Creates a contact. The server replies with a list of contacts and we
  # return the id of the first contact of the list
  def createContact(client, organizationId):
      contact = {
          'organizationId': organizationId,
          'name': 'John',
          'countryId': 'DK'
      }
      response = client.request('POST', '/contacts', {'contact': contact})
      return response['contacts'][0]['id']
  # Creates a product. The server replies with a list of products and we
  # return the id of the first product of the list
  def createProduct(client, organizationId):
      product = {
          'organizationId': organizationId,
          'name': 'Pens',
          'prices': [{
              'unitPrice': 200,
              'currencyId': 'DKK'
          }]
      }
      response = client.request('POST', '/products', {'product': product})
      return response['products'][0]['id']
  # Creates an invoice, the server replies with a list of invoices and we
  # return the id of the first invoice of the list
  def createInvoice(client, organizationId, contactId, productId):
      invoice = {
          'organizationId': organizationId,
          'invoiceNo': 65432,
          'entryDate': '2013-11-14',
          'contactId': contactId,
          'lines': [{
              'productId': productId,
              'unitPrice': 200
          }]
      }
      response = client.request('POST', '/invoices', {'invoice': invoice})
      return response['invoices'][0]['id']
  # Get id of organization associated with the API token.
  def getOrganizationId(client):
      response = client.request('GET', '/organization', None)
      return response['organization']['id']
  # Gets a invoice by its Id
  def get_invoice(client, invoiceId):
      response = client.request('GET', '/invoices', invoiceId)
      return response['invoices'][0]
  def main():
      client = BillyClient('INSERT ACCESS TOKEN HERE')
      currentOrganizationId = getOrganizationId(client)
      newContactId = createContact(client, currentOrganizationId)
      newProductId = createProduct(client, currentOrganizationId)
      newinvoiceId = createInvoice(client, currentOrganizationId, newContactId, newProductId)
      newlyCreatedInvoice = get_invoice(client, newinvoiceId)
      print(newlyCreatedInvoice)
  main()

Use case examples

Posting a payment for an invoice

When you want to mark an invoice as paid, you have to create a bank payment which matches the invoice's amount. Let's say you've just created an invoice with ID inv-1234, with a total amount due of $1,200. This is how you would create the payment:

POST https://api.billysbilling.com/v2/bankPayments

{
      "bankPayment": {
        "organizationId": "YOUR COMPANY ID",
        "entryDate": "2014-01-16",
        "cashAmount": 1200,
        "cashSide": "debit",
        "cashAccountId": "BANK ACCOUNT ID",
        "associations": [
          {
            "subjectReference": "invoice:inv-1234"
          }
        ]
      }
    }

cashAccountId is the account that an amount of cashAmount was deposited to/withdrawn from. cashSide: "debit" means a deposit (used for invoices). cashSide: "credit" means a withdrawal (used for bills).

Each item in the associations array is a balance modifier. Since payments can pay different types of models (invoices, bills, etc.), you add the invoice to the payment by using a "reference," which is a type concatenated with a colon, concatenated with an ID; for example, invoice:inv-1234

The payment's currency is determined by the associations. This means that all associations must point to subjects in the same currency. That is, you can't pay a USD invoice together with an EUR invoice. This currency is called subjectCurrency, if you GET the bank payment later from the API. If the subjectCurrency is different from the currency of cashAccount, you also need to set a cashExchangeRate. In this example, we assume that the cashAccount is in USD.

After this call, our example invoice (for $1,200) will be paid, which will be visible through the invoice's isPaid property - which will be true - and its balance property, which will be 0.0.

The organizationId is only necessary if you are using an access token that's tied to a user (rather than a company), or if you are using Basic Auth. You can list your user's companies through GET /v2/user/organizations.

Posting a product with default price(s)

When POSTing to /v2/products, you can embed product prices this way:

POST https://api.billysbilling.com/v2/products

{
      "product": {
        "organizationId": "YOUR ORGANIZATION ID",
        "name": "Bat capes",
        "accountId": "REVENUE ACCOUNT ID",
        "salesTaxRulesetId": "SALES TAX RULESET ID"
        "prices": [
          {
            "unitPrice": 14000,
            "currencyId": "USD"
          },
          {
            "unitPrice": 10000,
            "currencyId": "EUR"
          }
        ]
      }
    }

The organizationId is only necessary if you are using an access token that's tied to a user (rather than a company), or if you are using Basic Auth. You can list your user's companies through GET /v2/user/organizations.

Resource Documentation

Following is an extensive list of resources you can interact with.

In addition to the standard resources, there are also a number of "special" routes by which you can interact with the resources - for example, sending an invoice as email. These are not yet documented.

Table of Contents

Resources

/v2/accountGroups

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
naturebelongs-to-immutable, required
namestring-required
descriptionstring- 

/v2/accountNatures

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
reportTypeenum- 
namestring- 
normalBalanceenum- 

/v2/accounts

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
namestring-required
accountNointeger- 
descriptionstring- 
groupbelongs-to-required
naturebelongs-to-immutable, default: unknown
systemRoleenum- 
currencybelongs-to-immutable
taxRatebelongs-to- 
isPaymentEnabledboolean- 
isBankAccountboolean-immutable
isArchivedboolean- 
bankNamestring-readonly
bankRoutingNostring-readonly
bankAccountNostring-readonly
bankSwiftstring-readonly
bankIbanstring-readonly

/v2/attachments

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
createdTimedatetime-readonly
ownerbelongs-to-reference-immutable, required
filebelongs-toThe ID of the file to attach.immutable, required
priorityinteger- 

/v2/balanceModifiers

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
modifierbelongs-to-reference-immutable, required
subjectbelongs-to-reference-immutable, required
amountfloatBalance modifier amount.readonly
entryDatedateDate of balance modifier entryreadonly
realizedCurrencyDifferencefloat-readonly
isVoidedboolean-readonly

/v2/bankLineMatches

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
accountbelongs-toThe ID of the account to create the bank line match for.immutable, required
differenceTypeenumIf there is a difference, this value determines its type.immutable
feeAccountbelongs-toThe ID of the account to add the bank fee to.immutable, required
entryDatedate-immutable
amountfloat-immutable
sideenum-immutable
isApprovedbooleanWhether the bank lines and subjects are approved. 
approvedTimedatetimeTime of approval.readonly
lineshas-manyLines for this bank line match. If this parameter is set, any existing lines for this bank line match will be deleted before adding the new ones.readonly
subjectAssociationshas-manySubject associations for the bank line match. If this parameter is set, any existing subject associations for this bank line match will be deleted before adding the new ones.readonly

/v2/bankLines

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
matchbelongs-to-required
accountbelongs-toThe ID of the account to create the bank line match for.required
entryDatedate-required
descriptionstring-required
amountfloat-required
sideenum-required

/v2/bankLineSubjectAssociations

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
matchbelongs-to-required
subjectbelongs-to-referenceThe reference of the subject.required

/v2/bankPayments

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
contactbelongs-toThe contact that all of the associations' subjects belong to. You can omit setting this property if you supply one or more records in associations. It will then default to thecontact of the associations' subject.immutable, required, default: unknown
createdTimedatetimeWhen the payment record was created in the system.readonly
entryDatedateThe date of the transaction.immutable, required
cashAmountfloatThe amount that was deposited into or withdrawn fromcashAccount in the account's currency.immutable, required
cashSideenumIndicates whether the payment was a deposit (debit) or a withdrawal (credit).immutable, required
cashAccountbelongs-toThe account that an amount was deposited into or withdrawn from. Must have isPaymentEnabled set to true.immutable, required
cashExchangeRatefloatThe exchange rate between subjectCurrency andcashAccount's currency. 1 subjectCurrency =cashExchangeRate cashAccountCurrency. Must be set if cashAccount's currency is different thansubjectCurrency. Will be ignored and set to 1 if the currencies are the same.immutable, required, default: unknown
subjectCurrencybelongs-toThe currency of what was paid. You can omit setting this property if you supply one or more records in associations. It will then default to the currency of theassociations' subject. In case of an overpayment, the overpaid amount will be added to the contact's balance in this currency.immutable, required, default: unknown
feeAmountfloatUsed if the bank or payment provider charged the organization a fee for handling this payment. The fee amount must be positive, and will always be recorded as an expense (i.e., a debit posting on an expense account). feeAmount is in the same currency ascashAccount's currency. The fee is always considered the organization's expense. This means that: - For deposits the subjects' balances will also be offset against the fee, as the customer shouldn't pay for the organization's payment expenses. Example: An invoice of 100 USD will be paid in full by acashAmount of 95 and afeeAmount of 5. - For withdrawals the subject's balances will not be offset against the fee, as the supplier shouldn't pay for the organization's payment expenses. Example: A bill of 100 USD will be paid in full by acashAmount of 105 and afeeAmount of 5.immutable
feeAccountbelongs-toThe account to record the fee expense on. Must be an expense account. Must be set if feeAmount is set.immutable, required
isVoidedbooleanIndicates if the payment has been canceled. You must leave this field blank or set to false for new payments. Once a payment has been canceled by setting this field to true it can't be reinstated ("un-canceled"). 
associationshas-manyThe subjects this payment involves. The subjects' outstandingbalance and isPaid properties will automatically be updated by the API.immutable
contactBalancePostingshas-many-readonly

/v2/billLines

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
billbelongs-to-immutable, required
accountbelongs-to-required
taxRatebelongs-to- 
descriptionstring-required
amountfloat-required
taxfloat-readonly
priorityinteger- 

/v2/bills

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
typeenum-immutable
createdTimedatetime-readonly
approvedTimedatetime-readonly
contactbelongs-to-immutable, required
contactNamestring- 
entryDatedate-required
paymentAccountbelongs-to-immutable
paymentDatedate-immutable, required
dueDatedate- 
isBareboolean- 
stateenum-default: "draft"
suppliersInvoiceNostring- 
taxModeenum- 
voucherNostringVoucher number for the bill 
amountfloat-readonly
taxfloat-readonly
currencybelongs-to- 
exchangeRatefloat- 
balancefloat-readonly
isPaidboolean-readonly
lineDescriptionstring-readonly
creditedBillbelongs-to-immutable
creditNoteshas-many-readonly
lineshas-many- 
attachmentshas-many- 
balanceModifiershas-manyPayment associations for the bill.readonly
sourcestringSource of the bill.readonly
subjectstringOriginal subject of the bill.readonly

/v2/cities

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
namestring- 
countystring- 
statebelongs-to- 
countrybelongs-to- 

/v2/contactBalancePayments

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
contactbelongs-toAutomatically set by the API. The contact that all of theassociations' subjects belong to.immutable, required, default: unknown
createdTimedatetime-readonly
entryDatedateThe date that this transaction occurred accounting-wise.immutable, required
amountfloatThe amount to apply to the subject(s) balance.immutable, required
sideenumAutomatically set by the API. Indicates: - debit: The payment used a prepayment that a customer made to the company. -credit: The payment used a prepayment that the company made to a vendor.immutable, required, default: unknown
currencybelongs-toAutomatically set by the API to the currency of the subject(s).immutable, required, default: unknown
isVoidedboolean- 
associationshas-manyThe subjects this payment involves. The subjects' outstandingbalance and isPaid properties will automatically be updated by the API.immutable, required

/v2/contactBalancePostings

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
contactbelongs-to-readonly
originatorbelongs-to-reference-readonly
createdTimedatetime-readonly
entryDatedate-readonly
amountfloat-readonly
sideenum-readonly
currencybelongs-to-readonly
isVoidedboolean-readonly

/v2/contactPersons

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
contactbelongs-to-immutable, required
isPrimarybooleanIf contact person is primary for the parent contact. 
namestringThe name of the contact person. Either name or email must be set.required
emailstringThe contact person's e-mail. Used to mail invoices to the contact. Either name or email must be set. 

/v2/contacts

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
typeenumWhether contact is a company or an person. Defaults to companyrequired, default: "company"
organizationbelongs-to-immutable, required
createdTimedatetime-readonly
namestringThe name of the contact. Can be either a company name or a person's name.required
countrybelongs-toThe contact's home/business country.required
streetstringThe contact's street address. 
citybelongs-toThe contact's city, if finite. 
cityTextstringThe contact's city, in text form. 
statebelongs-toThe name of the contact's state, if finite. 
stateTextstringThe name of the contact's state, in text form. 
zipcodebelongs-toThe contact's zipcode, if finite. 
zipcodeTextstringThe contact's zipcode, in text form. 
contactNostringArbitrary number (or string) that contacts can be referred to by. 
phonestringThe contact's phone number. 
faxstringThe contact's fax number. 
currencybelongs-toDefault currency to use for invoices created for the contact. Has no effect in the API, as currency for invoice always is required. 
registrationNostringThe contact's EU VAT number, CVR number in Denmark, tax ID (TIN/EIN/SSN) in the US. 
eanstringThe contact's EAN (European Article Number). 
localebelongs-toLanguage to use in communications with the contact. The language also decides which language should be used on invoices created for the contact. 
isCustomerbooleanWhether the contact is regarded as a customer and can have invoices, etc. 
isSupplierbooleanWhether the contact is regarded as a vendor and can have bills etc. 
paymentTermsModeenum- 
paymentTermsDaysinteger-required
contactPersonshas-manyYou can add one or more contact persons for the contact. If this parameter is set, any existing contact persons for this contact will be deleted before adding the new ones. 
accessCodestringUsed to generate the contact's customer portal URL. 
emailAttachmentDeliveryModeenumWhether to deliver attachments by link to customer portal or with email attachments. 
isArchivedbooleanWhether the contact is archived. 

/v2/countryGroups

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
namestring- 
iconstring- 
memberCountryIdsstring- 

/v2/countries

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
namestring- 
hasStatesboolean- 
hasFiniteStatesboolean- 
hasFiniteZipcodesboolean- 
iconstring- 
localebelongs-to- 

/v2/currencies

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
namestring- 
exchangeRatefloat- 

/v2/daybookBalanceAccounts

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
daybookbelongs-to-required
accountbelongs-to-required
priorityinteger-required

/v2/daybooks

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
isTransactionSummaryEnabledboolean-required
namestringThe name of the daybook.required
defaultContraAccountbelongs-to- 
balanceAccountshas-manyBalance accounts to monitor. 

/v2/daybookTransactionLines

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
daybookTransactionbelongs-to-immutable, required
textstringLine description. 
accountbelongs-to@id@ of account line is to be applied to. Either @accountId@ or @accountNo@ must be filled in.immutable, required
taxRatebelongs-to-immutable
contraAccountbelongs-to@id@ of account line is to be applied against.immutable
amountfloatAmount of line.immutable, required
sideenum"debit" or "credit"immutable, required
currencybelongs-to-immutable, default: unknown
priorityinteger- 

/v2/daybookTransactions

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
daybookbelongs-to- 
createdTimedatetime-readonly
entryDatedateThe transaction entry date in YYYY-MM-DD format.immutable, required
voucherNostringA number or a string that identifies this transaction's voucher number, e.g. a bill. 
descriptionstringDescription of transaction. 
extendedDescriptionstringExtended/verbose description of transaction. 
apiTypestringMethod used to make the API call. This is for your notation only. 
stateenum-default: "draft"
priorityinteger- 
lineshas-manyLines for the transaction. At minimum one line must be supplied.immutable
attachmentshas-manyAttachments for the daybook transaction.immutable

/v2/files

Supports: get by id, list, create, bulk save, bulk delete

PropertyTypeDescriptionNotes
createdTimedatetime-readonly
fileNamestring-readonly
fileSizeinteger-readonly
fileTypestring-readonly
isImageboolean-readonly
isPdfboolean-readonly
imageWidthinteger-readonly
imageHeightinteger-readonly
downloadUrlstring-readonly

/v2/invoiceLateFees

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
invoicebelongs-to-immutable, required
createdTimedatetime-readonly
entryDatedate-immutable, required
flatFeefloat-immutable, required
percentageFeefloat-immutable, required
amountfloat-readonly
isVoidedboolean- 

/v2/invoiceLines

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
invoicebelongs-to-immutable, required
productbelongs-toThe product to use for the line.required
descriptionstringOptional description to display under the product's name on the invoice. 
quantityfloatThe line's quantity.default: 1
unitPricefloatThe price per unit of the product.required
amountfloat-readonly
taxfloat-readonly
taxRatebelongs-to-readonly
discountTextstringText to display if the line includes a discount. 
discountModeenumHow the discount should be calculated. Cash discount: The value of @discountValue@ will be subtracted from the line's amount. Percentage discount: The percentage value of @discountValue@ will be subtracted from the line's amount. 
discountValuefloatDepending on @discountMode@, either an amount or a percentage value. Percentage value should be supplied as e.g. 25 for 25%. Required if @discountValue@ is set. ignored if @discountValue@ is not set. 
priorityinteger- 

/v2/invoiceReminderAssociations

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
reminderbelongs-to-required
invoicebelongs-to-required
lateFeebelongs-to-readonly

/v2/invoiceReminders

Supports: get by id, list, create, bulk save, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
contactbelongs-to-required
createdTimedatetime-readonly
associationshas-many- 
flatFeefloat- 
percentageFeefloat- 
feeCurrencybelongs-to-required
sendEmailboolean- 
contactPersonbelongs-to-required
emailSubjectstring-required
emailBodystring-required
copyToUserbelongs-to- 
downloadUrlstring- 

/v2/invoices

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
typeenumWhether to create an invoice or a credit note. Defaults to @invoice@.immutable
createdTimedatetime-readonly
approvedTimedatetime-readonly
contactbelongs-toThe ID of the contact to create the invoice for.immutable, required
attContactPersonbelongs-toID for a contact person belonging to the contact that should be included as Att: in the invoice address area. 
entryDatedateThe invoice date. This parameter must not be set if the invoice has already been created.required
paymentTermsModeenum-default: unknown
paymentTermsDaysintegerNumber of days (positive or negative) for the mode defined bypaymentTermsModerequired
dueDatedateThe due date for payment of the invoice.readonly
stateenumUsed to change the state of the invoice. Currently the only state change possible is from draft to approved. Once an invoice has been approved, its state can't be changed. Omit from request body if state shouldn't be changed.required, default: "draft"
sentStateenumSent state of the email. Invoice is marked as unsent by default, can be marked as printed, sent,opened, viewed.required, default: "unsent"
invoiceNostringManually set the invoice number. Invoice numbers has to be unique. If no invoice number is set when the invoice is created, it will automatically be assigned a number using the company's invoice number model. This parameter must not be set if the invoice has already been created.immutable, required
taxModeenum- 
amountfloat-readonly
taxfloat-readonly
currencybelongs-toThe currency of the invoice. All lines' @unitPrice@ parameters should be in this currency.required, default: unknown
exchangeRatefloatThe exchange rate used for invoices in foreign currencies. The value should calculated like this: bq. @exchangeRate@ = @currency@ / @organization's base currency@ If this field is left blank, then today's exchange rate will automatically be used. If @currencyId@ equals the organization's base currency, the value of this field is ignored - it will always be 1. 
balancefloat-readonly
isPaidboolean-readonly
creditedInvoicebelongs-to-immutable
contactMessagestringOptional message to the contact, to be displayed at the top of the invoice PDF. 
lineDescriptionstringAutomatically generated based on its lines' descriptions. 
downloadUrlstring- 
lineshas-manyLines for the invoice. At minimum, one line must be supplied. If this parameter is set, any existing lines for this invoice will be deleted before adding the new ones. This parameter must not be set if the invoice has already been created.required
attachmentshas-manyAttachments for the invoice. 
lateFeeshas-many-readonly
balanceModifiershas-manyPayment associations for the invoice.readonly

/v2/locales

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
namestring- 
iconstring- 

/v2/organizations

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
ownerUserbelongs-to-required
createdTimedatetimeWhen the organization was createdreadonly
namestring-required
urlstring-readonly
streetstring- 
zipcodestring- 
citystring- 
countrybelongs-to-immutable, required
phonestring- 
faxstring- 
emailstring- 
registrationNostring- 
baseCurrencybelongs-to-immutable, required
logoFilebelongs-toOrganization logo. 
logoPdfFilebelongs-toLogo file to be used with PDFs.readonly
logoUrlstringFull-size logo URLreadonly
iconFilebelongs-toOrganization icon. 
iconUrlstringFull-size icon URLreadonly
icon48Urlstring48x48 pixels icon URLreadonly
fiscalYearEndMonthinteger-required
firstFiscalYearStartdate-required
firstFiscalYearEnddate-required
hasBillVoucherNobooleanWhether or not the company has a bill voucher number 
subscriptionCardTypestring- 
subscriptionCardNumberstring- 
subscriptionCardExpiresdate- 
subscriptionTransactionbelongs-toThe transaction for the company's recurring subscription. 
isSubscriptionBankPayerboolean- 
subscriptionPricefloat- 
subscriptionPeriodenum-required, default: "monthly"
subscriptionDiscountfloat-readonly
subscriptionExpiresdate- 
isTrialboolean-readonly
isTerminatedboolean- 
terminationTimedatetimeWhen the company was terminatedreadonly
localebelongs-toThe organization's default language. Will be used for all contacts unless overridden on a contact level.required
billEmailAddressstringAn email can be sent to this address and its attachments will be processed into billsreadonly
isUnmigratedbooleanIf this is true, the company has not yet migrated to our new system.readonly
isLockedbooleanIf this is true, the company is locked.readonly
lockedCodeenum-readonly
lockedReasonstringReason the company is currently locked.readonly
appUrlstring-readonly
emailAttachmentDeliveryModeenumWhether to deliver attachments by link to customer portal, or with email attachments.required
hasVatboolean- 
vatPeriodenum-required
defaultInvoiceBankAccountbelongs-to- 
invoiceNoModeenum-required
nextInvoiceNointeger-required
paymentTermsModeenum-required
paymentTermsDaysinteger-required
defaultSalesAccountbelongs-to- 
defaultSalesTaxRulesetbelongs-to- 
bankSyncStartDatedate- 
defaultBankFeeAccountbelongs-to- 
defaultBillBankAccountbelongs-to- 

/v2/postings

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
transactionbelongs-to-readonly
entryDatedate-readonly
textstring-readonly
accountbelongs-to-readonly
amountfloat-readonly
sideenum-readonly
currencybelongs-to-readonly
salesTaxReturnbelongs-to-readonly
isVoidedboolean-readonly
isBankMatchedboolean-readonly
priorityinteger-readonly

/v2/productPrices

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
productbelongs-to-immutable, required
unitPricefloatCurrency for the unit price.required
currencybelongs-toThe default unit price for invoice lines when the invoice's currency matches this currency.required

/v2/products

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
namestringThe name of the product.required
descriptionstringOptional description that will be used as default on invoice lines with this product. 
accountbelongs-toThe account that sales of the product should be coded to.required, default: unknown
productNostringA number (or string) that the organization identifies the product by. 
suppliersProductNostringA number (or string) that the organization's supplier identifies the product by. 
salesTaxRulesetbelongs-to-default: unknown
isArchivedboolean- 
priceshas-manyThe product can have a unit price for each of the organization's currencies. 

/v2/salesTaxAccounts

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
accountbelongs-to-required
typeenum-required
priorityinteger- 

/v2/salesTaxMetaFields

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
namestring-required
descriptionstring- 
priorityinteger- 
isPredefinedboolean- 

/v2/salesTaxPayments

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
salesTaxReturnbelongs-to-immutable, required
entryDatedate-immutable, required
accountbelongs-to-immutable, required
amountfloat-readonly
sideenum-readonly
isVoidedboolean- 

/v2/salesTaxReturns

Supports: get by id, list, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-readonly
createdTimedatetime-readonly
periodTypeenum-readonly
periodstring-readonly
periodTextstring- 
correctionNointeger-readonly
startDatedate-readonly
endDatedate-readonly
reportDeadlinedate- 
isSettledboolean- 
isPaidboolean-readonly

/v2/salesTaxRules

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
rulesetbelongs-to-immutable, required
countrybelongs-to-immutable, required
statebelongs-to-immutable
countryGroupbelongs-to-immutable
contactTypeenum-immutable
taxRatebelongs-to-immutable
priorityinteger-immutable

/v2/salesTaxRulesets

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
namestring-immutable, required
abbreviationstring-immutable
descriptionstring-immutable
fallbackTaxRatebelongs-to-immutable
isPredefinedboolean-readonly
ruleshas-many-immutable

/v2/states

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
stateCodestring- 
namestring- 
countrybelongs-to- 

/v2/taxRateDeductionComponents

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
taxRatebelongs-to-immutable, required
sharefloat-immutable, required
sourceenum-immutable, required
accountbelongs-to-immutable, required
priorityinteger- 

/v2/taxRates

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
namestring-immutable, required
abbreviationstring-immutable
descriptionstring-immutable
ratefloat-immutable, required
appliesToSalesboolean-immutable
appliesToPurchasesboolean-immutable
isPredefinedboolean-readonly
isActiveboolean- 
netAmountMetaFieldbelongs-to-immutable
deductionComponentshas-many-immutable

/v2/transactions

Supports: get by id, list, create, update, bulk save, delete, bulk delete

PropertyTypeDescriptionNotes
organizationbelongs-to-immutable, required
transactionNointeger-readonly
voucherNostring-readonly
createdTimedatetime-readonly
entryDatedate-readonly
originatorbelongs-to-reference-readonly
originatorNamestring-readonly
isVoidedboolean-readonly
isVoidboolean-readonly
postingshas-many-readonly

/v2/users

Supports: get by id, list, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
createdTimedatetimeDate/time user was createdreadonly
namestringUser's full name.required
emailstringUser's email address.required
phonestringUser's phone number. 
profilePicFilebelongs-toProfile picture of user.readonly
profilePicUrlstring-readonly
profilePic48Urlstring-readonly
isStaffbooleanWhether or not the user is a member of the Billy staff. 
isSupporterbooleanWhether or not the user is a Billy supporter. 
isAdminbooleanWhether or not the user is a Billy admin. 
isSupportAccessAllowedbooleanWhether or not the user chooses to allow supporter access. 

/v2/zipcodes

Supports: get by id, list, create, update, bulk save, bulk delete

PropertyTypeDescriptionNotes
zipcodestring- 
citybelongs-to- 
statebelongs-to- 
countrybelongs-to- 
latitudefloat- 
longitudefloat-