NAV
shell

Quaderno API Features

Use this Base URL for production:

https://{{ACCOUNT_NAME}}.quadernoapp.com/api

Welcome to the Quaderno API! You can use our API to access all our features, designed around the idea of making tax management and invoicing easy for small businesses.

The Quaderno API is based on the REST architectural style, and comprised of resources with predictable, human-readable and resource-oriented URLs. We make use of standard HTTP features (HTTP Authentication, Verbs, and Response Codes), understandable by any off-the-shelf HTTP client. We return JSON for everything (including errors!), and expect to receive JSON for all POST and PUT requests.

Use this Base URL for Quaderno Sandbox testing:

https://{{ACCOUNT_NAME}}.sandbox-quadernoapp.com/api

If you have any suggestions, tips, or questions that you feel aren't answered here or in our developer site, please get in touch and let us know!

Authentication

To authorize, use this code:

curl https://ACCOUNT_NAME.quadernoapp.com/api/invoices.json \
  -u YOUR_API_KEY:x

You can also ping to check if the service is up, your credentials are correct, or to know your remaining requests without doing an actual request:

curl https://quadernoapp.com/api/ping \
  -u YOUR_API_KEY:x

Quaderno uses an API key to authorise all requests, allowing access to the API in combination with the account name in the endpoint URL. You can find your API keys in quadernoapp.com/users/api-keys.

Quaderno expects the API key to be included via HTTP Basic Auth in all API requests to the server. When using curl, it looks like the following:

-u YOUR_API_KEY:x

Using that modifier on curl will send a request with an HTTP header like Authorization: Basic xxxx

Authorization

curl https://quadernoapp.com/api/authorization \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "identity": {
    "id": "999",
    "name": "Sheldon Cooper",
    "email": "s.cooperphd@yahoo.com",
    "publishable_key":"pk_1111111111111111",
    "href": "http://nippur-999.quadernoapp.com/api/"
  }
}

If you don't know the ACCOUNT_NAME for your target account, you can get it with the authorization API call.

This returns the following as a JSON payload:

Parameter Description
id An identity, which is not used for determining who this user is within Quaderno. The id field should therefore not be used for submitting data within Quaderno's API.
name The user's full name.
email The user's email address.
publishable_key The users's publishable key.
href The custom API endpoint URL for the user, providing the sought-after ACCOUNT_NAME between http:// and .quadernoapp....

Rate Limiting

To make it easier to determine if your application is being rate-limited, or is approaching that level, we have the following HTTP headers on our successful responses:

There is a limit of 100 API calls per 15 seconds.

We reserve the right to tune the limitations, but we promise to keep them high enough to allow a well-behaving interactive app to do it's job.

If you exceed the limit you will receive a HTTP 429 (Too Many Requests).

Pagination

A call with the created_before parameter set:

curl https://ACCOUNT_NAME.quadernoapp.com/api/contacts.json?created_before=42 \
  -u YOUR_API_KEY:x

Pagination is performed with a created_before parameter. This parameter takes an existing object ID value and returns objects listed after the named object, in reverse chronological order.

The HTTP header X-Pages-HasMore indicates whether more records can be fetched by using the same query with a lower created_before.

The HTTP header X-Pages-NextPage contains the URL that should be used to fetch the next page of records. It is only present if more records exist.

Bear in mind that Quaderno paginates GET index results.

You can change the number of objects to be returned with the limit parameter, defaulting to 25. This value is capped at 100 objects.

Errors

We don't usually have any trouble on our end, but when we do we'll let you know!

The Quaderno API uses the following error codes:

Code Text Description
400 Bad Request Your request may be malformed
401 Unauthorized Your API key is wrong, or your user does not have access to this resource
403 Forbidden The record requested is hidden for administrators only
404 Not Found The specified record could not be found
405 Method Not Allowed You tried to access a record with an invalid method
406 Not Acceptable You requested a format that isn't JSON
410 Gone The record requested has been removed from our servers
422 Unprocessable Entity The requested method cannot process for the record in question
429 Too Many Requests You're requesting too many records! Slow down!
500 Internal Server Error We had a problem with our server. Try again later.
502 Bad Gateway We had a different problem with our server. Try again later.
503 Service Unavailable We're temporarily offline for maintenance, or you've exceeded the rate limit. Please try again later.
504 Gateway Timeout Yep, you guessed it. We'll be back soon!

API Versioning

The Quaderno API is versioned. Our API versions are named for the date the version is released, like 20210701.

A new API version is released when we introduce a backwards-incompatible change to the API. For example, changing a field type or name, or deprecating endpoints.

Backwards-incompatible (also called breaking changes) example:

// Prior to version 20210316 the tax calculation endpoint was called like:
/taxes/calculate?country=US&postal_code=94010

// From version 20210316 taxes are calculated with:
/tax_rates/calculate?to_country=US&to_postal_code=90210&tax_code=standard&amount=10

The worldwide tax panorama changes fast, and so we adapt. While we're adding functionality to our API, we won't release a new API version. You'll be able to take advantage of this non-breaking backwards-compatible changes directly on the API version you're currently using.

Quaderno considers the following changes to be backwards-compatible:

Your integration should gracefully handle backwards-compatible changes.

Upgrading your API version

You programatically override the account's default API version with an Accept: api_version=version_number header.

curl https://ACCOUNT_NAME.quadernoapp.com/api/contacts.json?created_before=2048 \
  -u YOUR_API_KEY:x \
  -H 'Accept: application/json; api_version=20160602'

If you’re running an older version of the API, upgrade to the latest version to take advantage of new functionality or to streamline responses so the API is faster for you.

All changes are published on the API Changelog.

Upgrading your API version might break your current integration so we recommend testing on Quaderno Sandbox first.

To make the API upgrade definitive or see what version you’re running, visit your Profile → API Keys.

Version sunsetting and backported changes

From time to time, we will sunset old API versions. All Quaderno accounts using that version will be notified with at least one month in advance. Our own Engineering team will provide full support during transition.

Exceptionally, we might be forced to backport backwards-incompatible changes to all API versions for legal or security reasons, as well as for hotfixes needed to adapt to 3rd parties changes. This is the case of the removal of the DELETE /invoices/INVOICE_ID endpoint last summer. These changes receive the same treatment as sunsetting API versions.

Stay informed

To get important announcements regarding version sunsetting, backported changes, and new API versions, please ask you account admin to add a developer role to their account with a valid email so that API announcements reach its audience.

You can also check our What's New being informed of new features and improvements added to the app, and developer and integrations news.

Safely upgrading to API version 20220325

In order to avoid confusion with the variable with the confusing explanations of the notice field in the /tax_rates/calculate response, we've revamped the calculator internally to track the calculation process and return the reasoning behind a result a status code. If you were using the notice in your API automations, you'll need to swap to the status field as notice is no longer returned.

Safely upgrading to API version 20210701

In our goal to make Quaderno compliant with tax rules worldwide, we're going to limit the edition of invoices and credit notes via API as of 1 July.

From that date, an invoice is final (not editable) when any of the following occur:

If you need to make any changes to invoice's items, taxes, issue date or tax ID, you'll need to issue a credit note and create a new invoice.

You will still be allowed to edit the customer's billing address, PO number, tags, payment details, additional notes, and custom metadata.

What this breaking change implies:

Note that on 1 July the transition period for 20210316 ends as well, and the legacy endpoints for taxes and pagination will also respond 410 Gone.

If you're using these endpoints, you'll have to include extra logic in your code to issue a credit note and create a new invoice.

However, given your use case is one that automatically issues paid invoices (for instance, your customer's pays and then your system issues the invoice, not the other way around) and you're up for a challenge, our recommendation to face this change is to migrate to the new Transactions API.

Why? Because you can create the contact (if doesn't exists yet), create the invoice, and insert the payment information, all in only one performant API call 🎉!

Safely upgrading to API version 20210316

Most changes simply add functionality, thus maintaining backwards compatibility. Occasionally, we release a version with breaking changes, such as the current 20210316.

Example of the new Tax Rates endpoint

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates/calculate?to_country=US&to_postal_code=90210&tax_code=standard&amount=10 \
  -u YOUR_API_KEY:x

Note you'll get a 0.0 rate along with this notice in the response for both cases, unless you are registered on the tax jurisdiction:

"notice": "You haven't registered this tax jurisdiction in your Quaderno account. If you have to collect taxes here, please set it up in https://ACCOUNT_NAME.quadernoapp.com/settings/jurisdictions ."

Do not fret, the transition to 20210316 is as easy as pie 🥧.

For calculating tax rates, altough the new endpoint accepts many more parameters, just changing the URL from /taxes/calculate to /tax_rates/calculate will do. Well, not so fast. It may seem it worked quickly, but you're probably getting a 0% tax rate. That's because now you can only collect taxes on Tax Jurisdictions where you're registered. So don't be tempted to create a new custom Tax Rate, just head to your Quaderno Account and update the tax jurisdictions where your business is registered in. Now we're talking! Quaderno will always provide updated worldwide tax rates seamlessly for you.

Alternatively, you can register jurisdictions programatically, first listing all jurisdictions and then registering your Tax IDs on the jurisdictions. You can read more about Tax Jurisdictions here.

For paginating objects, we switched to a faster cursor based pagination approach and you'll need to change the page parameter for created_before, which references the ID of the object instead of the page number. You'll get a maximum of 100 objects in reverse chronological order (lower IDs means older objects). Note the response headers changed from X-Pages-CurrentPage and X-Pages-TotalPages to X-Pages-HasMore and X-Pages-NextPage.

You can test all this changes in our sandbox. There are two approaches:

And that's basically it 🎉! Now you can go focus on your business while we deal with your taxes.

Changelog

API versions and changes

20220325 (Current version)

20210701

20210316

20170914

20170628

20170418

20161108

20160614

20160602

Dev tools

We provide a testing environment, and a few endpoints and libraries to help with the development experience with the Quaderno set of APIs and products.

Testing environment

We provide a separate environment for testing purposes, with its own URL and credentials. We call it Quaderno Sandbox. Follow the link to learn about its special facilities to improve the development experience!

Purging Quaderno Sandbox

curl --user sk_your-secret-key:x \
     --request POST \
     --url http://{{your-account-name}}.sandbox-quadernoapp.com/api/purge

Our testing environment, Quaderno Sandbox, has a global 200 documents cap. You can manually purge all data with:

POST /purge

Pinging the API

curl --user sk_your-secret-key:x \
     --url https://quadernoapp.com/api/ping

You can ping Quaderno to check if the service is up, your credentials are correct, or to know your remaining API requests without doing an accountable request:

GET /ping

Libraries

To install our Ruby library, add the following to your Gemfile:

  gem 'quaderno', require: 'quaderno-ruby'

You can use our API wrappers to connect and handle our APIs in a nicer way. We provide a Ruby library and a PHP library.

You may eventually find also a Swift library in our Github repos, but it doesn't currently support the latest API additions.

Ruby Wrapper

To configure our Ruby gem, just add this to your initializers:

  Quaderno::Base.configure do |config|
    config.auth_token = 'my_authenticate_token'
    config.url = 'https://my_subdomain.quadernoapp.com/api/'
    config.api_version = API_VERSION # Optional, defaults to the API version set in your account
  end

Our Ruby library code is in https://github.com/quaderno/quaderno-ruby.

Please follow the README instructions to learn more.

PHP Wrapper

To setup the library:

require_once 'quaderno_load.php';
QuadernoBase::init('YOUR_API_KEY', 'YOUR_API_URL', API_VERSION);
QuadernoBase::ping();   // optionally test the API connection

Our PHP library code is in https://github.com/quaderno/quaderno-php and you may install it via Composer.

⚠️ There's a known limitation on the current version of the PHP wrapper which does not allow to easily inspect response headers, like the ones used to facilitate pagination (X-Pages-NextPage and X-Pages-HasMore). For now, you can workaround that either by playing with more requests with smaller data ranges and higher "limit" to fetch more docs (max 100); or fetch your first find data with any query and then perform another paginated request with the created_before parameter and the ID of the last returned document, until the response is empty.

Simple example to request all pages from the given dates until all invoices have been returned:

$invoices = [];
$filters['limit'] = '50'; // bunches of 50 invoices per request
$filters['date']= "$from,$to"; // please use dates to avoid always getting all invoices
do {
  $data = QuadernoInvoice::find($filters);
  if (is_array($data) && !empty($data)) {
    $invoices = array_merge($invoices, $data);
    $last_invoice = end($data); // advances array's internal pointer to the last element, and returns its value
    $filters['created_before'] = $last_invoice->id; // paginate from there
  }
} while (!empty($data));

Please follow the README instructions to learn more.

CORE RESOURCES

Core resources are those shared across several API endpoints, like contacts, products or events.

Contacts

A contact is any customer or vendor who appears on your invoices, credit notes, and expenses.

Create a contact

Contact creation example:

  curl --request POST \
  --url https://ACCOUNT_NAME.quadernoapp.com/api/contacts/ \
  --user YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
      "kind": "person",
      "first_name": "Susan",
      "last_name": "Sanders",
      "country": "United Kingdom",
      "email": "anemail@example.com"
    }'

The above command returns JSON structured like this:

{
  "id": 1249481,
  "city": null,
  "country": "GB",
  "created_at": 1632158561,
  "email": "anemail@example.com",
  "first_name": "Susan",
  "full_name": "Susan Sanders",
  "kind": "person",
  "language": "EN",
  "last_name": "Sanders",
  "notes": null,
  "permalink": "https://ACCOUNT_NAME.quadernoapp.com/billing/th3p3rm4l1nk",
  "phone_1": null,
  "postal_code": null,
  "processor": null,
  "processor_id": null,
  "region": null,
  "street_line_1": null,
  "street_line_2": null,
  "tax_id": null,
  "web": null
}

HTTP Request

POST /contacts

Parameters

Parameter Type Description
kind string The type of contact. Values are company or person. Defaults to company.
first_name string The contact's first name / business name. Required
last_name string The contact's last name. Empty when the contact is a company.
tax_id string Any tax identification number. Quaderno can validate tax IDs from the EU, United Kingdom, Switzerland, Québec (Canada), Australia, and New Zealand.
contact_person string If the contact is a company, this is its contact person.
street_line_1 string Address line 1 (Street address/PO Box).
street_line_2 string Address line 2 (Apartment/Suite/Unit/Building).
city string City/District/Suburb/Town/Village.
postal_code string ZIP or postal code.
region string Region, province or state.
country string 2-letter country code.
phone_1 string The contact's phone number.
email string The contact's email address. Multiple emails should be separated by commas. Maximum number of addresses allowed is 3.
web string The contact's website.
language string The contact's preferred language. Should be included in the account's translations list
notes string Internal notes about the contact.

Retrieve a contact

GET /contacts/CONTACT_ID

curl https://ACCOUNT_NAME.quadernoapp.com/api/contacts/CONTACT_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
    "id":456987213,
    "kind":"person",
    "first_name":"Sheldon",
    "last_name":"Cooper",
    "full_name":"Sheldon Cooper",
    "street_line_1":"2311 N. Los Robles Avenue",
    "street_line_2":"",
    "postal_code":"91104",
    "city":"Pasadena",
    "region":"CA",
    "country":"US",
    "phone_1":"",
    "email":"s.cooperphd@yahoo.com",
    "web":"",
    "discount":null,
    "tax_id":"",
    "language":"EN",
    "notes":"",
    "permalink":"https://ACCOUNT_NAME.quadernoapp.com/billing/th3p3rm4l1nk",
}

HTTP Request

GET /contacts/CONTACT_ID

Parameters

Parameter Type Description
CONTACT_ID integer The ID of the contact to retrieve. Required

Retrieve a contact by payment processor

curl https://ACCOUNT_NAME.quadernoapp.com/api/PAYMENT_PROCESSOR/customers/CUSTOMER_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
    "id":456987213,
    "kind":"person",
    "first_name":"Sheldon",
    "last_name":"Cooper",
    "full_name":"Sheldon Cooper",
    "street_line_1":"2311 N. Los Robles Avenue",
    "street_line_2":"",
    "postal_code":"91104",
    "city":"Pasadena",
    "region":"CA",
    "country":"US",
    "phone_1":"",
    "email":"s.cooperphd@yahoo.com",
    "web":"",
    "discount":null,
    "tax_id":"",
    "language":"EN",
    "notes":"",
    "permalink":"https://ACCOUNT_NAME.quadernoapp.com/billing/th3p3rm4l1nk",
}

HTTP Request

GET /PAYMENT_PROCESSOR/customers/CUSTOMER_ID

Parameters

Parameter Type Description
PAYMENT_PROCESSOR string The name of the payment processor. Can be stripe, paypal, gocardless and braintree. Required
CUSTOMER_ID string The ID of the contact in the payment processor. Required

Update a contact

curl https://ACCOUNT_NAME.quadernoapp.com/api/contacts/CONTACT_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  -H 'Content-Type: application/json' \
  -d '{ "first_name": "Anthony" }'

The above command returns JSON structured like this:

{
    "id":456987213,
    "kind":"person",
    "first_name":"Anthony",
    "last_name":"Cooper",
    "full_name":"Sheldon Cooper",
    "street_line_1":"2311 N. Los Robles Avenue",
    "street_line_2":"",
    "postal_code":"91104",
    "city":"Pasadena",
    "region":"CA",
    "country":"US",
    "phone_1":"",
    "email":"s.cooperphd@yahoo.com",
    "web":"",
    "discount":null,
    "tax_id":"",
    "language":"EN",
    "notes":"",
    "permalink":"https://ACCOUNT_NAME.quadernoapp.com/billing/th3p3rm4l1nk",
}

HTTP Request

PUT /contacts/CONTACT_ID

Parameters

Parameter Type Description
CONTACT_ID integer The ID of the contact to update. Required
kind string The type of contact. Values are company or person. Defaults to company.
first_name string The contact's first name. Required
last_name string The contact's last name. Empty when the contact is a company.
tax_id string Any tax identification number. Quaderno can validate tax IDs from the EU, United Kingdom, Switzerland, Québec (Canada), Australia, and New Zealand.
contact_person string If the contact is a company, this is its contact person.
street_line_1 string Address line 1 (Street address/PO Box).
street_line_2 string Address line 2 (Apartment/Suite/Unit/Building).
city string City/District/Suburb/Town/Village.
postal_code string ZIP or postal code.
region string Region, province or state.
country string 2-letter country code.
phone_1 string The contact's phone number.
email string The contact's email address. Multiple emails should be separated by commas. Maximum number of addresses allowed is 3.
web string The contact's website. Validates format
language string The contact's preferred language. Should be included in the translations list
notes string Internal notes about the contact.

Delete a contact

curl https://ACCOUNT_NAME.quadernoapp.com/api/contacts/CONTACT_ID \
  -u YOUR_API_KEY:x \
  -X DELETE \
  -H 'Content-Type: application/json' \

HTTP Request

DELETE /contacts/CONTACT_ID

Parameters

Parameter Type Description
CONTACT_ID integer The ID of the contact to delete. Required

List all contacts

curl https://ACCOUNT_NAME.quadernoapp.com/api/contacts \
  -u YOUR_API_KEY:x 

The above command returns JSON structured like this:

[
  {
    "id":456987213,
    "kind":"person",
    "first_name":"Sheldon",
    "last_name":"Cooper",
    "full_name":"Sheldon Cooper",
    "street_line_1":"2311 N. Los Robles Avenue",
    "street_line_2":"",
    "postal_code":"91104",
    "city":"Pasadena",
    "region":"CA",
    "country":"US",
    "phone_1":"",
    "email":"s.cooperphd@yahoo.com",
    "web":"",
    "discount":null,
    "tax_id":"",
    "language":"EN",
    "notes":"",
    "permalink":"https://ACCOUNT_NAME.quadernoapp.com/billing/th3p3rm4l1nk",
  },
  {
    "id":456982365,
    "kind":"company",
    "full_name":"Apple Inc.",
    "street_line_1":"1 Infinite Loop",
    "street_line_2":"",
    "postal_code":"95014",
    "city":"Cupertino",
    "region":"CA",
    "country":"US",
    "phone_1":"",
    "email":"info@apple.com",
    "web":"http://apple.com",
    "discount":null,
    "tax_id":"",
    "language":"EN",
    "notes":"",
    "permalink":"https://ACCOUNT_NAME.quadernoapp.com/billing/4n0th3rp3rm4l1nk",
  }
]

HTTP Request

GET /contacts

Parameters

Parameter Type Description
q string A case-sensitive filter on the list based on the contact's full name, email or tax ID.

Products

Products are all those goods or services that you sell to your customers, or that you buy to your providers to help run your business.

Products are internally equivalent to Document items from the Billing API, just with a slightly different set of fields. The advantage of this Products API is that you can manage the objects independently of the document they're being used on (invoices, expenses...). Both objects can be back-referenced via the product_code field.

Create a product

curl https://ACCOUNT_NAME.quadernoapp.com/api/items \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
    "code": "prod_ebook1",
    "name": "How to live with taxes",
    "description": "Ebook for dealing with taxes",
    "product_type": "service",
    "unit_cost": "9.1",
    "tax_class": "ebook",
    "tax_type": "included"
  }'

The above command returns JSON structured like this:

{
  "id": 40057,
  "code": "prod_ebook1",
  "description": "Ebook for dealing with taxes",
  "name": "How to live with taxes",
  "product_type": "service",
  "unit_cost": "9.1",
  "stock": null,
  "tax_class": "ebook",
  "kind": "one_off",
  "tax_type": "included",
  "tax_based_on": "customer_country",
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/items/40057"
}

HTTP Request

POST /items

Parameters

Parameter Type Description
code string The product’s SKU (Stock Keeping Unit) describe specific product variations, taking into account any combination of: attributes, currency, and cost. Required
name string The product’s name, meant to be displayable to the customer. Required
unit_cost string The unit amount in paise to be charged
description string The product’s description, meant to be displayable to the customer. Use this field to optionally store a long form explanation of the product being sold for your own rendering purposes.
curency string Three-letter ISO currency code, in uppercase. Must be a supported currency in your payment processors.
tax_class string The tax class that applies to the product.
tax_type string Specify if taxes are included or excluded in the unit_cost. Can be included, excluded. Defaults to excluded.
product_type string Specifies the product type. Can be either good or service. Defaults to service.
kind string The type of transaction. Can be one_off or subscription. Defaults to one_off
stripe_id string Only for Stripe subscriptions. ID of the Stripe price related to the subscription.
paypal_interval_unit string Only for PayPal subscriptions. Specifies billing frequency. Can be daily, weekly, monthly, yearly.
paypal_interval_frequency integer Only for PayPal subscriptions. The number of intervals between subscription billings.
paypal_interval_duration integer Only for subscriptions. Number of times the charge should recur.

Retrieve a product

curl https://ACCOUNT_NAME.quadernoapp.com/api/items/PRODUCT_ID \ 
  -u YOUR_API_KEY:x 

The above command returns JSON structured like this:

{
  "id": 40057,
  "code": "prod_ebook1",
  "description": "Ebook for dealing with taxes",
  "name": "How to live with taxes",
  "product_type": "service",
  "unit_cost": "9.1",
  "stock": null,
  "tax_class": "ebook",
  "kind": "one_off",
  "tax_type": "included",
  "tax_based_on": "customer_country",
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/items/40057"
}

HTTP Request

GET /items/PRODUCT_ID

Parameters

Parameter Type Description
PRODUCT_ID integer The ID of the product to retrieve. Required

Update a product

curl https://ACCOUNT_NAME.quadernoapp.com/api/items/PRODUCT_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  --header 'Content-Type: application/json' \
  --data '{
    "kind": "subscription",
    "paypal_interval_duration": 6
  }'

The above command converts the Product to a PayPal subscription, returning JSON structured like this:

{
  "id": 40057,
  "code": "prod_ebook1",
  "description": "Ebook for dealing with taxes",
  "name": "How to live with taxes",
  "product_type": "service",
  "unit_cost": "9.1",
  "stock": null,
  "tax_class": "ebook",
  "kind": "subscription",
  "tax_type": "included",
  "tax_based_on": "customer_country",
  "stripe_plan_id": "",
  "paypal_interval_unit": "monthly",
  "paypal_interval_frequency": null,
  "paypal_interval_duration": 6,
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/items/40057"
}

HTTP Request

PUT /items/PRODUCT_ID

Parameters

Parameter Type Description
PRODUCT_ID integer The ID of the product to update. Required
code string The product’s SKU (Stock Keeping Unit) describe specific product variations, taking into account any combination of: attributes, currency, and cost. Required
name string The product’s name, meant to be displayable to the customer. Required
unit_cost string The unit amount in paise to be charged
description string The product’s description, meant to be displayable to the customer. Use this field to optionally store a long form explanation of the product being sold for your own rendering purposes.
currency string Three-letter ISO currency code, in uppercase. Must be a supported currency in your payment processors.
tax_class string The tax class that applies to the product.
tax_type string Specify if taxes are included or excluded in the unit_cost. Can be included, excluded. Defaults to excluded.
product_type string Specifies the product type. Can be either good or service. Defaults to service.
kind string The type of transaction. Can be one_off or subscription. Defaults to one_off
stripe_id string Only for Stripe subscriptions. ID of the Stripe price related to the subscription.
paypal_interval_unit string Only for PayPal subscriptions. Specifies billing frequency. Can be daily, weekly, monthly, yearly.
paypal_interval_frequency integer Only for PayPal subscriptions. The number of intervals between subscription billings.
paypal_interval_duration integer Only for subscriptions. Number of times the charge should recur.

Delete a product

curl https://ACCOUNT_NAME.quadernoapp.com/api/items/PRODUCT_ID \
  -u YOUR_API_KEY:x \
  -X DELETE

HTTP Request

DELETE /items/PRODUCT_ID

Parameters

Parameter Type Description
PRODUCT_ID integer The ID of the product to delete. Required

List all products

GET /items

curl https://ACCOUNT_NAME.quadernoapp.com/api/items \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id": 40057,
    "code": "prod_ebook1",
    "description": "Ebook for dealing with taxes",
    "name": "How to live with taxes",
    "product_type": "service",
    "unit_cost": "9.1",
    "stock": null,
    "tax_class": "ebook",
    "kind": "one_off",
    "tax_type": "included",
    "tax_based_on": "customer_country",
    "url": "https://ACCOUNT_NAME.quadernoapp.com/api/items/40057"
  },
  {
    "id": 186698,
    "code": "PRODUCT_SKU",
    "description": null,
    "name": "Titanic II: The revenge",
    "product_type": "service",
    "unit_cost": "99.0",
    "tax_class": "eservice",
    "kind": "one_off",
    "tax_type": "excluded",
    "tax_based_on": "customer_country",
  }
 ]

HTTP Request

GET /items

Parameters

Parameter Type Description
q string A case-sensitive filter on the list based on the product's code or name.

Events

Events are our way of letting you know when something interesting happens in your account. When an interesting event occurs, we create a new Event object and potentially send it to you in case you have registered its webhook.

For example, when a new invoice is created, we create a invoice.created event; and when an invoice is deleted, we create an invoice.deleted event.

The event object

{
  "event_type":"invoice.created",
  "account_id": 99999,
  "data":
  {
    "object":
    {
      "id":925,
      "contact_id":128,
      "tag_list":[],
      "number":"123346",
      "issue_date":"2016-02-12",
      "contact_name":"OrsonFarm",
      "contact": {
        "first_name":"OrsonFarm",
        "last_name":null,
        "email":"orso@farm.com",
        "contact_person":"Orson Welles"
      },
      "currency":"EUR",
      "gross_amount_cents":826,
      "total_cents":1000,
      "amount_paid_cents":0,
      "po_number":null,
      "payment_details":null,
      "notes":null,
      "state":"outstanding",
      "subject":null,
      "street_line_1":null,
      "street_line_2":null,
      "city":null,
      "postal_code":null,
      "region":null,
      "country":"ES",
      "processor_id":null,
      "processor":null,
      "processor_fee_cents":null,
      "custom_metadata": {
        "my_custom_id": "123456"
      },
      "due_date":null,
      "permalink":"5191567e45d6aa419927ce7a8ba2ee870c9f0a45",
      "email":null,
      "gross_amount":"8.26",
      "total":"10.00",
      "amount_paid":"0.00",
      "items":
      [
        {
          "id":1056,
          "description":"Test item acces token",
          "quantity":"1.0",
          "unit_price":"8.26",
          "discount_rate":"0.0",
          "tax_1_amount_cents":174,
          "tax_1_name":"IVA",
          "tax_1_rate":21.0,
          "tax_1_country":null,
          "tax_2_amount_cents":0,
          "tax_2_name":null,
          "tax_2_rate":null,
          "tax_2_country":null,
          "subtotal_cents":"826.0",
          "total_amount_cents":1000,
          "discount_cents":"0.0",
          "taxes_included":true,
          "reference":null,
          "tax_1_amount":"1.74",
          "tax_2_amount":"0.00",
          "unit_price_cents":"826.00",
          "discount":"0.00",
          "subtotal":"8.26",
          "total_amount":"10.00"
        }
      ],
      "payments":[]
    }
  }
}

{
  "event_type":"contact.updated",
  "account_id": 99999,
  "data":
  {
    "object":
    {
      "id":76,
      "kind":"person",
      "first_name":"Adella",
      "last_name":"Schowalter",
      "full_name":"Adella Schowalter",
      "contact_name":null,
      "street_line_1":null,
      "street_line_2":null,
      "postal_code":null,
      "city":null,
      "region":null,
      "country":"DE",
      "phone_1":null,
      "email":null,
      "web":null,
      "discount":null,
      "language":"EN",
      "tax_id":null,
      "currency":"USD",
      "notes":null,
      "processor_id":null,
      "processor":null
    }
  }
}

{
  "event_type":"payment.created",
  "account_id": 99999,
  "data":
  {
    "object":
    {
      "id":15,
      "document_id":815,
      "date":"2015-09-22",
      "payment_method":"credit_card",
      "amount_cents":400,
      "amount":"4.00"
    }
  }
}

{
  "event_type":"checkout.succeeded",
  "account_id": 99999,
  "data":{
    "object":{
      "transaction_details":{
        "session":43,
        "session_permalink":"https://demo.quadernoapp.com/checkout/session/8ccf3fdc42b85800188b113b81d3e4212ef094b3",
        "gateway":"stripe",
        "type":"charge",
        "description":"Unicorn",
        "customer":"cus_FXesSyaK3CG8Oz",
        "email":"john@doe.com",
        "transaction":"pi_1F2ZYtEjVHvINKlcq2as8H5V",
        "product_id":"prod_61ffa845b4a0b8",
        "tax_name":"VAT",
        "tax_rate":20.0,
        "extra_tax_name":null,
        "extra_tax_rate":null,
        "iat":1564647784,
        "amount_cents":1500,
        "amount":"15.00",
        "currency":"EUR"
      },
      "contact":{
        "id":547540,
        "kind":"company",
        "first_name":"John ",
        "last_name":"Doe",
        "full_name":"John  Doe",
        "contact_name":null,
        "street_line_1":"Fake Street 1",
        "postal_code":"SW15 5PU",
        "city":null,
        "region":null,
        "country":"GB",
        "email":"john@doe.com",
        "web":null,
        "language":"EN",
        "tax_id":null,
      }
    }
 }
}

{
  "event_type":"checkout.failed",
  "account_id": 99999,
  "data":{
    "object":{
      "message":{
        "response_message":"Insufficient funds",
        "status_code":422
      },
      "transaction_details":{
        "gateway":"stripe",
        "type":"charge",
        "description":"Unicorn",
      },
      "contact":{
        "id":547540,
        "kind":"company",
        "first_name":"John ",
        "last_name":"Doe",
        "full_name":"John  Doe",
        "contact_name":null,
        "street_line_1":"Fake Street 1",
        "postal_code":"SW15 5PU",
        "city":null,
        "region":null,
        "country":"GB",
        "email":"john@doe.com",
        "web":null,
        "language":"EN",
        "tax_id":null,
      }
    }
  }
}

{
  "event_type":"checkout.abandoned",
  "account_id": 99999,
  "data":{
    "object":{
      "transaction_details":{
        "description":"Unicorn",
        "plan":"awesome"
      },
      "contact":{
        "first_name":"John ",
        "last_name":"Doe",
        "city":null,
        "country":"GB",
        "email":"john@doe.com"
      }
    }
  }
}

All event uses the same data format, regardless of event type:

Parameter Type Description
event_type string The event which triggered the webhook (invoice.created, contact.deleted, payment.created, etc).
account_id integer ID of the account that originated the event.
data hash A simplified JSON representation of the object.

Types of events

This is a list of all the types of events we currently send via webhooks. We may add more at any time, so in developing and maintaining your code, you should not assume that only these types may exist.

Event types are a combination of the object you want to be notified about and the object state, like in resource.event. Our goal is to design a consistent system that makes things easier to anticipate and code against.

Available events are:

Endpoint errors

If your event-receiving endpoint does not respond with a 200 when Quaderno POSTs the data, Quaderno will try again within 48 hours.

If you fail to respond a second time then your subscription to that event will be deleted.

Checking the webhook signatures

Quaderno signs all webhook events it sends to your endpoints by including a signature in each event’s X-Quaderno-Signature header. This allows you to verify that requests were sent by Quaderno and not by a third-party impersonating us.

The key is returned when you create a webhook or when you list all your webhooks.

To verify a request, you must generate a signature using the same secret key used by Quaderno and comparing the result to the value of the X-Quaderno-Signature header.

In your code that processes webhooks:

  1. Create a string with the webhook's URL, exactly as you entered it in Quaderno (including any query string, if applicable). Quaderno always signs webhook requests with the exact URL you provided when you configured the webhook. A difference as small as including or removing a trailing slash will prevent the signature from validating.
  2. Append the POST body to the URL string, with no delimiter. The body should be a JSON-encoded representation of the data.
  3. Hash the resulting string with HMAC-SHA1, using your webhook's authentication key to generate a binary signature.
  4. Base64 encode the binary signature.
  5. Compare the binary signature that you generated to the signature provided in the X-Quaderno-Signature HTTP header.

TAXES

Tax IDs

Quaderno calculates taxes based on your tax settings. You need to set all your tax IDs from the jurisdictions where you're registered for tax collection in order to calculate taxes for that jurisdictions.

This API will allow you to manage your tax IDs and validate any tax IDs.

Create a tax ID

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_ids \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
    "jurisdiction_id": "6",
    "value": "IE6388047V"
  }'

The above command returns JSON structured like this:

{
  "id": 235464,
  "jurisdiction": {
    "id": 6,
    "country": "IE",
    "name": "Ireland"
  },
  "state": "verified",
  "valid_from": "2021-09-01",
  "valid_until": null,
  "value": "IE6388047V",
  "created_at": 1632985500
}

HTTP Request

POST /tax_ids

Parameters

Parameter Type Description
jurisdiction_id integer The tax jurisdiction. Required
value decimal The tax ID's value for the selected. Required
valid_from date The tax ID's validity date. Defaults to the beginning of the year.

Retrieve a tax ID

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_ids/TAX_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "id": 235464,
  "jurisdiction": {
    "id": 6,
    "country": "IE",
    "name": "Ireland"
  },
  "state": "verified",
  "valid_from": "2021-09-01",
  "valid_until": null,
  "value": "IE6388047V",
  "created_at": 1632985500
}

HTTP Request

GET /tax_ids/TAX_ID

Parameters

Parameter Type Description
TAX_ID integer The ID of the tax ID to retrieve. Required

Update a tax ID

Updating your tax ID is allowed to fix mistakes when setting it. Updating your jurisdiction is not allowed.

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_ids/TAX_ID
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  -X PUT \
  -d '{
        "value": "21416127"
      }'

The above command returns JSON structured like this:

{
  "id": 235464,
  "jurisdiction": {
    "id": 6,
    "country": "IE",
    "name": "Ireland"
  },
  "state": "verified",
  "valid_from": "2021-09-01",
  "valid_until": null,
  "value": "IE21416127",
  "created_at": 1632985500
}

HTTP Request

PUT /tax_ids/TAX_ID

Parameters

Parameter Type Description
TAX_ID integer The ID of the tax ID to update. Required
value decimal The tax ID's value for the selected. Required
valid_from date The tax ID's validity date.
valid_until date The tax ID's expiration date. Use null if the tax ID has no expiration date.

Delete a tax ID

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_ids/TAX_ID \
  -u YOUR_API_KEY:x \
  -X DELETE

HTTP Request

DELETE /tax_ids/TAX_ID

Parameters

Parameter Type Description
TAX_ID integer The ID of the tax ID to delete. Required

List all tax IDs

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_ids \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id": 456987213,
    "jurisdiction": {
      "id": 6,
      "name": "Ireland"
    },
    "state": "verified",
    "valid_from": "2015-01-01",
    "valid_until": null,
    "value": "IE6388047V",
    "created_at": 1611322639
  },
  {
    "id": 456982365,
    "jurisdiction": {
      "id": 73,
      "name": "Luxembourg"
    },
    "state": "verified",
    "valid_from": "2020-01-01",
    "valid_until": null,
    "value": "LU21416127",
    "created_at": 1611322639
  }
]

HTTP Request

GET /tax_ids

Validate a tax ID

Validating a tax ID is useful to learn if your customer is a business, so that you can apply reverse charge when selling to countries different that yours on B2B sales. Head to our Support Center to learn more about applying reverse charges.

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_ids/validate.json?country=IE&tax_id=IE6388047V \
  -u YOUR_API_KEY:x \

The above command returns JSON structured like this:

{
  "valid":true  // this means your customer is a business
}

HTTP Request

GET /tax_ids/validate

Parameters

Parameter Mandatory Description
country Yes The customer's country (2-letter ISO code)
tax_id Yes The customer's tax ID.

The result can be true (valid tax ID), false (invalid tax ID) or null (the external service is temporarily unavailable).

Please keep in mind that tax IDs are not actually validated while using the Sandbox. For testing purposes you can use the following IDs:

Tax Rates

This API will allow you to customize your available tax rates for tax calculation.

Take into account that taxes are calculated based on the tax IDs you registered on the different Tax Jurisdictions. You may want to override these tax rates for greater control over the taxes you charge, by creating a custom tax rate. We recommend using this option only as a last resort for special cases.

Create a custom tax rate

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
      "name": "XYZ",
      "jurisdiction_id": 50,
      "value": 10
    }'

The above command returns JSON structured like this:

{
  "id": 226655,
  "type": "custom",
  "jurisdiction": {
    "id": 50,
    "country": "US",
    "name": "United States – Texas",
    "region": "TX"
  },
  "name": "XYZ",
  "tax_code": "eservice",
  "valid_from": "2018-01-01",
  "valid_until": null,
  "value": 10.0,
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates/226655",
  "created_at": 1628787498
}

HTTP Request

POST /tax_rates

Parameters

Parameter Type Description
jurisdiction_id integer The tax jurisdiction. Required
name string The tax rate's name. Required
value decimal The tax rate's value for the selected tax_code. Required
tax_code string The tax code. Defaults to the account's default tax code.
valid_from date The tax rate's validity date. Defaults to the beginning of 3 years ago.

Retrieve a tax rate

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates/TAX_RATE_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "id": 226655,
  "type": "custom",
  "jurisdiction": {
    "id": 50,
    "country": "US",
    "name": "United States – Texas",
    "region": "TX"
  },
  "name": "XYZ",
  "tax_code": "eservice",
  "valid_from": "2018-01-01",
  "valid_until": null,
  "value": 10.0,
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates/226655",
  "created_at": 1628787498
}

HTTP Request

GET /tax_rates/TAX_RATE_ID

Parameters

Parameter Type Description
TAX_RATE_ID integer The ID of the tax rate to retrieve. Required

Update a tax rate

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates/TAX_RATE_ID \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  -X PUT \
  --data '{
      "jurisdiction_id": 50,
      "value": 13
    }'

The above command returns JSON structured like this:

{
  "id": 226655,
  "type": "custom",
  "jurisdiction": {
    "id": 50,
    "country": "US",
    "name": "United States – Texas",
    "region": "TX"
  },
  "name": "XYZ",
  "tax_code": "eservice",
  "valid_from": "2018-01-01",
  "valid_until": null,
  "value": 13.0,
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates/226655",
  "created_at": 1628787498
}

HTTP Request

PUT /tax_rates/TAX_RATE_ID

Parameters

Parameter Type Description
TAX_RATE_ID integer The ID of the tax rate to update. Required
jurisdiction_id integer The tax jurisdiction. Required
name string The tax rate's name. Required
value decimal The tax rate's value for the selected tax_code. Required
tax_code string The tax code. Defaults to the account's default tax code.
valid_from date The tax rate's validity date. Defaults to 3 years ago.
valid_until date The tax rate's expiration date. Use null if the tax rate has no expiration date.

Delete a tax rate

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates/TAX_RATE_ID \
  -u YOUR_API_KEY:x \
  -X DELETE

HTTP Request

DELETE /tax_rates/TAX_RATE_ID

Parameters

Parameter Type Description
TAX_RATE_ID integer The ID of the tax rate to delete. Required

List all tax rates

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id": 226655,
    "type": "custom",
    "jurisdiction": {
      "id": 50,
      "country": "US",
      "name": "United States – Texas",
      "region": "TX"
    },
    "name": "XYZ",
    "tax_code": "eservice",
    "valid_from": "2018-01-01",
    "valid_until": null,
    "value": 13.0,
    "url": "https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates/226655",
    "created_at": 1628787498
  },
  {},
  {}
]

HTTP Request

GET /tax_rates

List all available Tax Rates based on your registered Tax Jurisdictions.

 Filtering by tax jurisdiction

Filtering is available using the query parameter jurisdiction_id. Learn more about jurisdictions here.

Filtering by tax jurisdiction:

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates/?jurisdiction_id=50 \
  -u YOUR_API_KEY:x

Calculating a tax rate

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_rates/calculate?to_country=US&to_postal_code=90210&tax_code=standard&amount=10 \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "city": "BEVERLY HILLS",
  "country": "US",
  "county": "LOS ANGELES",
  "currency": "EUR",
  "name": "Sales tax",
  "notes": null,
  "product_type": "service", // informative field, either sent on request or default account's product type
  "rate": 9.5,
  "region": "CA",
  "tax_behavior": "exclusive",
  "tax_code": "standard",
  "taxable_part": 100.0,
  "taxable_base": 10.0,
  "tax_amount": 0.95,
  "total_amount": 10.95,
  "status":"taxable"
}

The tax calculation endpoint allows you to send the transaction's amount before taxes. Quaderno will use that amount to calculate the final tax-included amount you should charge to your customer.

Tax calculations are based on your tax settings. You can collect taxes only in tax jurisdictions where you're registered for tax collection. Please check your tax jurisdictions in your Quaderno account, or use the Tax Jurisdictions and Tax IDs APIs to register.


To understand the endpoint response, the formulae are:

taxable_base = amount * taxable_part
tax_amount   = taxable_base * rate/100
total_amount = amount + tax_amount

Taxable base is the amount you must use to calculate the tax amount. It's usually 100% (taxable_part) of the amount of your product before taxes but there are a few exceptions. For example, in Texas the taxable base is 80% for SaaS products.


For jurisdictions that need to apply two tax rates (e.g. some Canadian provinces), the response will include the second tax rate on fields with the additional_ suffix, like so:

{
  "additional_name": "QST",
  "additional_rate": 9.975,
  "additional_taxable_part": 100.0,
  "country": "CA",
  "currency": "CAD",
  "name": "GST",
  "notes": null,
  "product_type": "service",
  "rate": 5.0,
  "region": "QC",
  "tax_behavior": "exclusive",
  "tax_code": "standard",
  "taxable_part": 100.0,
  "import": false,
  "subtotal": 100.0,
  "tax_amount": 5.0,
  "additional_tax_amount": 9.98,
  "total_amount": 114.98,
  "status":"taxable"
}

⚠️ If you try to calculate taxes in a jurisdiction you're not registered, Quaderno will return an not_registered status, along with null name and a 0.0 rate:

{
  "country": "CA",
  "currency": "EUR",
  "name": null, // use this to identify wrongly configured Tax Jurisdictions
  "notes": null,
  "product_type": "service",
  "rate": 0.0,
  "region": null,
  "tax_behavior": "exclusive",
  "tax_code": "eservice",
  "taxable_part": null,
  "import": true,
  "subtotal": null,
  "tax_amount": null,
  "total_amount": null,
  "status":"not_registered"
}

HTTP Request

GET /tax_rates/calculate

Parameters

Parameter Type Description
to_country string The customer's country. 2-letter ISO country code. Required
to_postal_code string The customer's postal code. Required whether the customer is based in the US or Canada.
to_city string The customer's city. It provides more accurate calculations in the US.
tax_id string The customer's tax ID.
from_country string The country where the order shipped from. 2-letter ISO country code. Defaults to the account's country.
from_postal_code string The postal code where the order shipped from. Defaults to the account's postal code.
amount decimal The transaction's amount.
tax_code string The transaction's tax code. Defaults to the account's default tax code.
tax_behavior string Specifies whether the price is considered inclusive of taxes or exclusive of taxes. Can be inclusive or exclusive. Defaults to exclusive.
product_type string Specifies whether the product is a good or a service. Can be good or service. Defaults to the account's default. Read this guide to change it.
date string The transaction's date. Defaults to today.
currency string The transaction's currency. Three-letter ISO currency code, in uppercase. Defauts to the account's default currency.

The status field response

The status field in the response contains a code indicating the reason of the calculation result:

 Caching tax calculation requests

We always recommend using cache for tax calculation, no matter if you have one shopping cart for multiple items or multiple carts with one item each.

The recommended TTL is 24 hours.

Tax Jurisdictions

A tax jurisdiction is an area subject to its own tax regulations. A tax jurisdiction can be a country, region, group of countries, province, state, city, county, district or any other area with a designated authority for tax purposes.

A businesses cannot collect taxes in a particular jurisdiction without being registered for tax collection with their tax authorities first.

You cannot create new tax jurisdictions, this API allows you to get all the tax jurisdictions so that you can then associate your tax id on those where you must collect taxes, using https://developers.quaderno.io/api/#create-a-tax-id. This action is equivalent to setting up a jurisdiction in the app via https://quadernoapp.com/settings/jurisdictions/new

Retrieve a jurisdiction

curl https://ACCOUNT_NAME.quadernoapp.com/api/jurisdictions/JURISDICTION_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "id": 94,
  "name": "Canada – British Columbia",
  "country": "CA",
  "region": "BC"
}

HTTP Request

GET /jurisdictions/JURISDICTION_ID

Parameters

Parameter Type Description
JURISDICTION_ID integer The ID of the jurisdiction to retrieve. Required

List all jurisdictions

GET /jurisdictions.json?country=CA

curl https://ACCOUNT_NAME.quadernoapp.com/api/jurisdictions.json?country=CA \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id": 94,
    "name": "Canada – British Columbia",
    "country": "CA",
    "region": "BC"
  },
  {
    "id": 95,
    "name": "Canada – Quebec",
    "country": "CA",
    "region": "QC"
  },
  {
    "id": 96,
    "name": "Canada – Saskatchewan",
    "country": "CA",
    "region": "SK"
  },
  {
    "id": 100,
    "name": "Canada – Manitoba",
    "country": "CA",
    "region": "MB"
  },
  {
    "id": 129,
    "name": "Canada – GST/HST",
    "country": "CA"
  }
]

HTTP Request

GET /jurisdictions

Parameters

Parameter Type Description
country string A case-sensitive filter on the list based on the jurisdiction's country.
region string A case-sensitive filter on the list based on the jurisdiction's region. Accepts the special string none to return jurisdictions that specifically have no region.

Tax Codes

Tax codes classify goods and services for tax purposes.

Retrieve a tax code

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_codes/TAX_CODE_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

  {
    "id": "eservice",
    "description": "Service that is delivered over the internet. It is essentially automated, involves minimal human intervention and in the absence of information technology does not have viability.",
    "name": "Electronically supplied services"
  }

HTTP Request

GET /tax_codes/TAX_CODE_ID

Parameters

Parameter Type Description
TAX_CODE_ID integer The ID of the tax code to retrieve. Required

List all tax codes

curl https://ACCOUNT_NAME.quadernoapp.com/api/tax_codes \ 
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id": "consulting",
    "description": "Any consulting and professional service (e.g., lawyers, designers, engineers, tax advisors, etc.).",
    "name": "Consulting"
  },
  {
    "id": "ebook",
    "description": "An electronic book that is sold with unlimited use.",
    "name": "eBook"
  },
  {
    "id": "eservice",
    "description": "Service that is delivered over the internet. It is essentially automated, involves minimal human intervention and in the absence of information technology does not have viability.",
    "name": "Electronically supplied services"
  },
  {
    "id": "exempt",
    "description": "Any nontaxable good or service. No tax is applied for jurisdictions that impose a tax.",
    "name": "Non-taxable"
  },
  {
    "id": "reduced",
    "description": "Specific goods and services with a reduced tax rate.",
    "name": "Reduced tax rate"
  },
  {
    "id": "saas",
    "description": "Cloud services software delivered over the internet. The software is not customized for the specific buyer. Assumes no software is downloaded by the buyer.",
    "name": "Software as a service (SaaS)"
  },
  {
    "id": "standard",
    "description": "Any taxable good or service. For jurisdictions that impose a tax, the standard rate is applied.",
    "name": "Generally taxable"
  }
]

HTTP Request

GET /tax_codes

Evidence

Location evidence are proofs of the customer's location that should be stored in order to be compliant with tax rules in some tax jurisdictions (e.g. EU VAT MOSS).

Create an evidence

POST /evidence

curl https://ACCOUNT_NAME.quadernoapp.com/api/evidence \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
      "document_id": 2009439,
      "billing_country": "US",
      "ip_address": "255.255.255.255",
      "bank_country": "US"
    }'

The above command returns JSON structured like this:

{
  "id": 982855,
  "document_id": 2009439,
  "state": "confirmed",
  "billing_country": "US",
  "ip_address": "255.255.255.255",
  "ip_country": null, // autocompleted when given a valid public IP
  "bank_country": "US",
  "additional_evidence": null,
  "additional_evidence_country": null,
  "notes": null
}

HTTP Request

POST /evidence

Parameters

Parameter Type Description
document_id integer The ID of the related document. Required
billing_country string Customer's billing country (2-letter ISO code)
ip_address string Customer's IP address
bank_country string Customer's bank country (2-letter ISO code)
additional_evidence string An explanatory note about the additional evidence. Up to 255 chars. Required if additional_evidence_country is passed
additional_evidence_country string Additional evidence for the customer location (2-letter ISO code). Required if additional_evidence is passed)
notes string Additional notes about the evidence

Retrieve an evidence

curl https://ACCOUNT_NAME.quadernoapp.com/api/evidence/EVIDENCE_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

  {
    "id":483,
    "document_id":9748,
    "state":"conflicting", // there are two non-matching evidences
    "billing_country":"ES",
    "ip_address":null,
    "ip_country":null,
    "bank_country":"US",
    "additional_evidence":null,
    "additional_evidence_country":null,
    "notes":null
  }

You may get conflicting alerts via the notes field, like in this example:

{
  "id": 981519,
  "document_id": 2008033,
  "state": "conflicting",
  "billing_country": "FR",
  "ip_address": "255.255.255.255",
  "ip_country": null,
  "bank_country": "FR",
  "additional_evidence": null,
  "additional_evidence_country": null,
  "notes": "Current tax (US) is different from customer location tax (FR)"
}

HTTP Request

GET /evidences/EVIDENCE_ID

Parameters

Parameter Type Description
EVIDENCE_ID integer The ID of the evidence to retrieve. Required

Update an evidence

curl https://ACCOUNT_NAME.quadernoapp.com/api/evidence/EVIDENCE_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  --data '{
    "billing_country": "FR",
    "bank_country": "FR"
  }'

The above command returns JSON structured like this:

  {
    "id":483,
    "document_id":9748,
    "state":"confirmed", // now two evidences match
    "billing_country":"FR",
    "ip_address": null,
    "ip_country": null,
    "bank_country":"FR",
    "additional_evidence":null,
    "additional_evidence_country":null,
    "notes":null
  }

HTTP Request

PUT /evidences/EVIDENCE_ID

Parameters

Parameter Type Description
EVIDENCE_ID integer The ID of the evidence to update. Required
document_id integer The ID of the related document. Required
billing_country string Customer's billing country (2-letter ISO code)
ip_address string Customer's IP address
bank_country string Customer's bank country (2-letter ISO code)
additional_evidence string An explanatory note about the additional evidence. Up to 255 chars. Required if additional_evidence_country is passed
additional_evidence_country string Additional evidence for the customer location (2-letter ISO code). Required if additional_evidence is passed)
notes string Additional notes about the evidence

List all evidence

curl https://ACCOUNT_NAME.quadernoapp.com/api/evidence \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id":487,
    "document_id":9756,
    "state":"unsettled", // example for missing evidences
    "billing_country":"US",
    "ip_address":null,
    "ip_country":null,
    "bank_country":null,
    "additional_evidence":null,
    "additional_evidence_country":null,
    "notes":null
  },
  {
    "id":486,
    "document_id":9755,
    "state":"confirmed",
    "billing_country":"ES",
    "ip_address":"92.186.16.30",
    "ip_country":"ES",
    "bank_country":null,
    "additional_evidence":null,
    "additional_evidence_country":null,
    "notes":null
  },
]

HTTP Request

GET /evidence

Parameters

Parameter Type Description
state string A case-sensitive filter on the list based on the evidence's state. You can combine multiple states separated by commas like ?state=confirmed,unsettled. Valid states are confirmed, unsettled and conflicting.
document_id string A case-sensitive filter on the list based on the document related to the evidence.

BILLING

Invoices

An invoice is a detailed list of goods shipped or services rendered, with an account of all costs.

Create an invoice

curl https://ACCOUNT_NAME.quadernoapp.com/api/invoices \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
    "contact": {
      "first_name": "John"
    },
    "currency": "EUR",
    "items_attributes": [
      {
        "product_code": "prod_ebook1"
      },
      {
        "description": "A new product example",
        "product_code": "prod_zzz",
        "quantity": 1,
        "total_amount": 100,
        "discount_rate": 10,
        "currency": "EUR",
        "tax_1_name": "IVA",
        "tax_1_rate": 21.0,
        "tax_1_country": "",
        "tax_1_region": null,
        "tax_1_transaction_type": "standard"
      }
    ], 
    "payment_method": "credit_card",
    "custom_metadata": {
      "a_custom_key": "a custom value"
    },
    "verification_code": "08V9SBg49G",
    "verification_permalink": "https://example.com/verification/08V9SBg49G"
  }'

The above command returns JSON structured like this:

{
  "id": 2009439,
  "blocked": false,
  "number": "00022",
  "issue_date": "2021-09-23",
  "created_at": 1632395785,
  "contact": {
    "id": 1247386,
    "full_name": "John"
  },
  "tax_id": null,
  "country": "ES",
  "street_line_1": null,
  "street_line_2": null,
  "city": null,
  "region": null,
  "postal_code": null,
  "po_number": null,
  "due_date": null,
  "currency": "EUR",
  "subject": null,
  "items": [
    {
      "id": 5889679,
      "product_code": "prod_ebook1",
      "description": "How to live with taxes",
      "reference": "prod_ebook1",
      "quantity": "1.0",
      "unit_price_cents": "910.0",
      "discount_rate": "0.0",
      "subtotal_cents": "910.0",
      "discount_cents": "0.0",
      "tax_1_name": null,
      "tax_1_rate": null,
      "tax_1_country": null,
      "tax_1_region": null,
      "tax_1_transaction_type": "eservice",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice",
      "gross_amount_cents": "910.0"
    },
    {
      "id": 5889680,
      "product_code": "prod_zzz",
      "description": "A new product example",
      "reference": "prod_zzz",
      "quantity": "1.0",
      "unit_price_cents": "9182.0",
      "discount_rate": "10.0",
      "subtotal_cents": "9182.2",
      "discount_cents": "918.2",
      "tax_1_name": "IVA",
      "tax_1_rate": 21.0,
      "tax_1_country": "ES",
      "tax_1_region": null,
      "tax_1_transaction_type": "standard",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice",
      "gross_amount_cents": "8264.0"
    }
  ],
  "subtotal_cents": "10092.0",
  "discount_cents": "918.0",
  "taxes": [
    {
      "label": "IVA (21%)",
      "rate": 21.0,
      "country": "ES",
      "region": null,
      "transaction_type": "standard",
      "amount_cents": 1735
    }
  ],
  "total_cents": 10909,
  "processor_fee_cents": null,
  "payments": [
    {
      "id": 1411973,
      "date": "2021-09-23",
      "payment_method": "credit_card",
      "amount_cents": 10909,
      "url": "https://ACCOUNT_NAME.quadernoapp.com/api/invoices/2009439/payments/1411973.json"
    }
  ],
  "payment_details": null,
  "notes": null,
  "state": "outstanding",
  "tag_list": [],
  "secure_id": "33e50eb45e36965033cafda76a56ffa818ea5e86",
  "permalink": "https://ACCOUNT_NAME.quadernoapp.com/invoice/33e50eb45e36965033cafda76a56ffa818ea5e86?otp=152406",
  "pdf": "https://ACCOUNT_NAME.quadernoapp.com/invoice/33e50eb45e36965033cafda76a56ffa818ea5e86.pdf?otp=152406",
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/invoices/2009439.json",
  "processor": null,
  "processor_id": null,
  "custom_metadata": {
    "a_custom_key": "a custom value"
  },
  "verification_code": "08V9SBg49G",
  "verification_permalink": "https://example.com/verification/08V9SBg49G"
}

HTTP Request

POST /invoices

Parameters

Parameter Type Description
number string A unique, sequential code that identifies the invoice. Legally, an invoice number sequence should never contain repeats or gaps. Automatic numbering is used by default. Manual numbering is discouraged.
issue_date date The date of the invoice's issue – not necessarily the date the products or services were provided.
currency string Three-letter ISO currency code, in uppercase. Defaults to the account's default currency.
contact object The data of the customer who will be billed.
items_attributes array Array of document items. Required
payment_method string Use this parameter to mark this document as paid in a single request. Can be credit_card, cash, wire_transfer, direct_debit, check, iou, paypal or other
payment_processor string The name of the payment processor used to take the payment.
payment_processor_id string The ID of the transaction in the payment_processor.
po_number string The number of the related order.
tag_list string Multiple tags should be separated by commas
payment_details string Detailed information about the accepted payment methods.
notes string Extra notes about the invoice.
attachment object The data of the file to be attached.
custom_metadata hash Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. You can have up to 20 keys, with key names up to 40 characters long and values up to 500 characters long.
verification_code string The code needed to validate the invoice, usually generated by tax authorities.
verification_permalink string The link needed to validate the invoice, usually generated by tax authorities.

Retrieve an invoice

curl https://ACCOUNT_NAME.quadernoapp.com/api/invoices/INVOICE_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "id":"507693322f412e0e2e0000da",
  "number":"0000047",
  "issue_date":"2012-10-11",
  "contact":{
    "id":"5073f9c22f412e02d00004cf",
    "full_name":"Teenage Mutant Ninja Turtles"
  },
  "country":"US",
  "street_line_1":"Melrose Ave, Sewer #3",
  "street_line_2":"",
  "city":"New York",
  "region":"New York",
  "postal_code":"",
  "po_number":null,
  "due_date":null,
  "currency":"USD",
  "exchange_rate":"0.680309",
  "items":[
    {
      "id":"48151623429",
      "description":"pizza",
      "quantity":"15.0",
      "unit_price_cents":"6000",
      "discount_rate":"0.0",
      "tax_1_name":"",
      "tax_1_rate":"",
      "tax_2_name":"",
      "tax_2_rate":"",
      "reference":"Even the bad ones taste good!",
      "subtotal_cents":"6000",
      "discount_cents":"0",
      "gross_amount_cents":"6000"
    }
  ],
  "subtotal_cents":"6000",
  "discount_cents":"0",
  "taxes":[],
  "total_cents":"6000",
  "payments":[],
  "payment_details":"",
  "notes":"",
  "state":"outstanding",
  "tag_list":["lasagna", "cat"],
  "secure_id":"7hef1rs7p3rm4l1nk",
  "permalink":"https://ACCOUNT_NAME.quadernoapp.com/invoice/7hef1rs7p3rm4l1nk",
  "pdf":"https://ACCOUNT_NAME.quadernoapp.com/invoice/7hef1rs7p3rm4l1nk.pdf",
  "custom_metadata":{},
  "verification_code": "08V9SBg49G",
  "verification_permalink": "https://example.com/verification/08V9SBg49G"

}

HTTP Request

GET /invoices/INVOICE_ID

Parameters

Parameter Type Description
INVOICE_ID integer The ID of the invoice to retrieve. Required

Update an invoice

curl https://ACCOUNT_NAME.quadernoapp.com/api/invoices \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  -X PUT \
  --data '{
      "street_line_1": "New Address"
    }'
  ...

The above command returns the whole object, now modified.

HTTP Request

PUT /invoices/INVOICE_ID

In our goal to make Quaderno compliant with tax rules worldwide, edition of invoices is limited to: po_number, tag_list, payment_details, notes, custom_metadata and all those related to the billing address: street_line_1,street_line_2,city,region, and postal_code.

If you need to make any changes to items, taxes, issue date or customer's tax ID, you'll need to issue a credit note and create a new invoice.

Parameters

Parameter Type Description
INVOICE_ID integer The ID of the invoice to update. Required
contact object The data of the customer who will be billed.
street_line_1 string The customer's billing address line 1 (Street address/PO Box).
street_line_2 string The customer's billing address line 2 (Apartment/Suite/Unit/Building).
city string The customer's billing city.
region string The customer's billing state/province/region.
postal_code string The customer's billing ZIP or postal code. Available for updates.
payment_processor string The name of the payment processor used to take the payment.
payment_processor_id string The ID of the transaction in the payment_processor.
po_number string The number of the related order.
tag_list string Multiple tags should be separated by commas
payment_details string Detailed information about the accepted payment methods.
attachment object The data of the file to be attached.
notes string Extra notes about the invoice.
custom_metadata hash Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. You can have up to 20 keys, with key names up to 40 characters long and values up to 500 characters long.
verification_code string The code needed to validate the invoice, usually generated by tax authorities.
verification_permalink string The link needed to validate the invoice, usually generated by tax authorities.

Delete an invoice

In our goal to make Quaderno compliant with tax rules worldwide, it is not possible to delete invoices. If you want to cancel out an invoice, please issue a credit note.

Deliver an invoice

Use this endpoint to send an invoice to your customer.

Emails will be sent from notifications@quaderno.io. If your customer replies to an invoice email, the reply will go to the email address you've set up on the business data page.

curl https://ACCOUNT_NAME.quadernoapp.com/api/invoices/INVOICE_ID/deliver \
  -u YOUR_API_KEY:x

HTTP Request

GET /invoices/INVOICE_ID/deliver

Parameters

Parameter Type Description
INVOICE_ID integer The ID of the invoice to deliver. Required

List all invoices

curl -u YOUR_API_KEY:x \
     -X GET 'https://ACCOUNT_NAME.quadernoapp.com/api/invoices'

The above command returns JSON structured like this:

[
  {
    "id":"507693322f412e0e2e00000f",
    "number":"0000047",
    "issue_date":"2012-10-11",
    "created_at":"1325376000",
    "contact":{
      "id":"5073f9c22f412e02d0000032",
      "full_name":"Garfield"
    },
    "country":"US",
    "street_line_1":"Street 23",
    "street_line_2":"",
    "city":"New York",
    "region":"New York",
    "postal_code":"10203",
    "po_number":null,
    "due_date":null,
    "currency":"USD",
    "exchange_rate":"0.680309",
    "items":[
      {
        "id":"48151623429",
        "description":"lasagna",
        "quantity":"25.0",
        "unit_price_cents":"375",
        "discount_rate":"0.0",
        "tax_1_name":"",
        "tax_1_rate":"",
        "tax_2_name":"",
        "tax_2_rate":"",
        "reference":"Awesome!",
        "subtotal_cents":"9375",
        "discount_cents":"0",
        "gross_amount_cents":"9375"
      }
    ],
    "subtotal_cents":"9375",
    "discount_cents":"0",
    "taxes":[],
    "total_cents":"9375",
    "payments":[
      {
        "id":"50aca7d92f412eda5200002c",
        "date":"2012-11-21",
        "payment_method":"credit_card",
        "payment_processor":"stripe",
        "payment_processor_id":"ch_19yUdh2eZvKYlo2CkFVBOZG7",
        "amount_cents":"9375",
      },
    ],
    "payment_details":"Ask Jon",
    "notes":"",
    "state":"paid",
    "tag_list":["lasagna", "cat"],
    "secure_id":"7hef1rs7p3rm4l1nk",
    "permalink":"https://quadernoapp.com/invoice/7hef1rs7p3rm4l1nk",
    "pdf":"https://quadernoapp.com/invoice/7hef1rs7p3rm4l1nk.pdf",
    "custom_metadata":{},
    "verification_code": "08V9SBg49G",
    "verification_permalink": "https://example.com/verification/08V9SBg49G"
  },

  {
    "id":"507693322f412e0e2e0000da",
    "number":"0000047",
    "issue_date":"2012-10-11",
    "created_at":"1325376020",
    "contact":{
      "id":"5073f9c22f412e02d00004cf",
      "full_name":"Teenage Mutant Ninja Turtles"
    },
    "country":"US",
    "street_line_1":"Melrose Ave, Sewer #3",
    "street_line_2":"",
    "city":"New York",
    "region":"New York",
    "postal_code":"",
    "po_number":null,
    "due_date":null,
    "currency":"USD",
    "exchange_rate":"0.680309",
    "items":[
      {
        "id":"481516234291",
        "description":"pizza",
        "quantity":"15.0",
        "unit_price_cents":"6000",
        "discount_rate":"0.0",
        "tax_1_name":"",
        "tax_1_rate":"",
        "tax_2_name":"",
        "tax_2_rate":"",
        "reference":"Even the bad ones taste good!",
        "subtotal_cents":"6000",
        "discount_cents":"0",
        "gross_amount_cents":"6000"
      }
    ],
    "subtotal_cents":"6000",
    "discount_cents":"0",
    "taxes":[],
    "total_cents":"6000",
    "payments":[],
    "payment_details":"",
    "notes":"",
    "state":"outstanding",
    "tag_list":["pizza", "turtles"],
    "secure_id":"7hes3c0ndp3rm4l1nk",
    "permalink":"https://ACCOUNT_NAME.quadernoapp.com/invoice/7hes3c0ndp3rm4l1nk",
    "pdf":"https://ACCOUNT_NAME.quadernoapp.com/invoice/7hes3c0ndp3rm4l1nk.pdf",
    "custom_metadata":{},
    "verification_code": "7XShGnyfgo",
    "verification_permalink": "https://example.com/verification/7XShGnyfgo"
  }
]

HTTP Request

GET /invoices

Parameters

Parameter Type Description
q string A case-sensitive filter on the list based on the invoice's number or customer name.
date string A case-sensitive filter on the list based on the invoice's issue date. Pass the date range in the url like ?date=DATE1,DATE2. Date range allows these formats: 2019-01-01,2019-12-31 or 2019/01/01,2019/12/31.
state string A case-sensitive filter on the list based on the invoice's state. Can be outstanding, late, paid, refunded and archived.
contact integer A case-sensitive filter on the list based on the customer's ID.

Credit Notes

A credit note is an official legal document used to notify your customer that credit is being applied. Usually a credit note is issued as a way to cancel out or correct an invoice by doing a full refund.

Credits always have an associated invoice which will be updated to state paid when the credit is created.

Create a credit

curl https://ACCOUNT_NAME.quadernoapp.com/api/credits \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  -d '{
    "invoice_id": 1
  }'

The above command returns JSON structured like this:

{
  "id": 3,
  "blocked": false,
  "number": "00001",
  "issue_date": "2021-09-23",
  "created_at": 1632386555,
  "related_document": {
    "id": 1,
    "type": "Invoice"
  },
  "contact": {
    "id": 1,
    "full_name": "John Smith"
  },
  "tax_id": null,
  "country": "ES",
  "street_line_1": null,
  "street_line_2": null,
  "city": null,
  "region": null,
  "postal_code": null,
  "po_number": null,
  "due_date": null,
  "currency": "USD",
  "subject": null,
  "exchange_rate": "0.852588",
  "items": [
    {
      "id": 4,
      "description": "Awesome ebook",
      "quantity": "1.0",
      "unit_price_cents": "1900.0",
      "discount_rate": "0.0",
      "tax_1_name": null,
      "tax_1_rate": 0.0,
      "tax_1_country": "US",
      "tax_1_region": "NY",
      "tax_1_transaction_type": "ebook",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": "US",
      "tax_2_region": "NY",
      "tax_2_transaction_type": "ebook",
      "reference": null,
      "subtotal_cents": "1900.0",
      "discount_cents": "0.0",
      "gross_amount_cents": "1900.0"
    },
    {
      "id": 5,
      "description": "Awesome SaaS",
      "quantity": "1.0",
      "unit_price_cents": "8175.0",
      "discount_rate": "0.0",
      "tax_1_name": "Sales tax",
      "tax_1_rate": 8.875,
      "tax_1_country": "US",
      "tax_1_region": "NY",
      "tax_1_transaction_type": "saas",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": "US",
      "tax_2_region": "NY",
      "tax_2_transaction_type": "saas",
      "reference": null,
      "subtotal_cents": "8175.0",
      "discount_cents": "0.0",
      "gross_amount_cents": "8175.0"
    }
  ],
  "subtotal_cents": "10075.0",
  "discount_cents": "0.0",
  "taxes": [
    {
      "label": "Sales tax (8.875%)",
      "rate": 8.875,
      "country": "US",
      "region": "NY",
      "transaction_type": "saas",
      "amount_cents": 725
    }
  ],
  "total_cents": 10800,
  "payments": [],
  "payment_details": null,
  "notes": null,
  "state": "outstanding",
  "tag_list": [
    "tag-a",
    "tag-b",
    "tag-c"
  ],
  "secure_id": "7hef1rs7p3rm4l1nk",
  "permalink": "https://ACCOUNT_NAME.quadernoapp.com/credit/b4a1539db4bca64f22a38c7f81e012a2555981dc",
  "pdf": "https://ACCOUNT_NAME.quadernoapp.com/credit/b4a1539db4bca64f22a38c7f81e012a2555981dc.pdf",
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/credits/3.json",
  "processor": null,
  "processor_id": null,
  "custom_metadata": {},
  "verification_code": "08V9SBg49G",
  "verification_permalink": "https://example.com/verification/08V9SBg49G"
}

HTTP Request

POST /credits

Parameters

You can basically use the same parameters as with invoices, but this is all you'll need on most cases:

Parameter Type Description
invoice_id integer The ID of the refunded invoice. Required
credited_amount decimal The total amount to be credited. Only for refunds of invoices with one single item. The credited amount must be equal or lower than the total amount of the related invoice.
payment_method string Use this parameter to mark this document as paid in a single request. Can be credit_card, cash, wire_transfer, direct_debit, check, iou, paypal or other.

Retrieve a credit

curl https://ACCOUNT_NAME.quadernoapp.com/api/credits/CREDIT_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "id":"507693322f412e0e2e0000da",
  "number":"0000047",
  "issue_date":"2012-10-11",
  "created_at":"1325376000",
  "related_document":{
    "id":999999,
    "type":"Invoice"
  },
  "contact":{
    "id":"5073f9c22f412e02d00004cf",
    "full_name":"Teenage Mutant Ninja Turtles"
  },
  "country":"US",
  "street_line_1":"Melrose Ave, Sewer #3",
  "street_line_2":"",
  "city":"New York",
  "region":"New York",
  "postal_code":"",
  "po_number":null,
  "due_date":null,
  "currency":"USD",
  "exchange_rate":"0.680309",
  "items":[
    {
      "id":"48151623429",
      "description":"pizza",
      "quantity":"15.0",
      "unit_price_cents":"6000",
      "discount_rate":"0.0",
      "tax_1_name":"",
      "tax_1_rate":"",
      "tax_2_name":"",
      "tax_2_rate":"",
      "reference":"Even the bad ones taste good!",
      "subtotal_cents":"6000",
      "discount_cents":"0",
      "gross_amount_cents":"6000"
    }
  ],
  "subtotal_cents":"6000",
  "discount_cents":"0",
  "taxes":[],
  "total_cents":"6000",
  "payments":[],
  "payment_details":"",
  "notes":"",
  "state":"outstanding",
  "tag_list":["lasagna", "cat"],
  "secure_id":"7hef1rs7p3rm4l1nk",
  "permalink":"https://ACCOUNT_NAME.quadernoapp.com/credit/7hef1rs7p3rm4l1nk",
  "pdf":"https://ACCOUNT_NAME.quadernoapp.com/credit/7hef1rs7p3rm4l1nk.pdf",
  "custom_metadata":{},
  "verification_code": "08V9SBg49G",
  "verification_permalink": "https://example.com/verification/08V9SBg49G"
}

HTTP Request

GET /credits/CREDIT_ID

Parameters

Parameter Type Description
CREDIT_ID integer The ID of the credit to retrieve. Required

Update a credit

curl https://ACCOUNT_NAME.quadernoapp.com/api/credits/CREDIT_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  -d '{ "notes": "You better pay this time, Tony",
        "custom_metadata": {
          "memo": "I think he is not paying again."
        }
      }'

The above command returns JSON structured like this:

{
  "id":"507693322f412e0e2e0000da",
  "number":"0000047",
  "issue_date":"2012-10-11",
  "created_at":"1325376000",
  "related_document":{
    "id":999999,
    "type":"Invoice"
  },
  "contact":{
    "id":"5073f9c22f412e02d00004cf",
    "full_name":"Teenage Mutant Ninja Turtles"
  },
  "country":"US",
  "street_line_1":"Melrose Ave, Sewer #3",
  "street_line_2":"",
  "city":"New York",
  "region":"New York",
  "postal_code":"",
  "po_number":null,
  "due_date":null,
  "currency":"USD",
  "exchange_rate":"0.680309",
  "items":[
    {
      "id":"48151623429",
      "description":"pizza",
      "quantity":"15.0",
      "unit_price_cents":"6000",
      "discount_rate":"0.0",
      "tax_1_name":"",
      "tax_1_rate":"",
      "tax_2_name":"",
      "tax_2_rate":"",
      "reference":"Even the bad ones taste good!",
      "subtotal_cents":"6000",
      "discount_cents":"0",
      "gross_amount_cents":"6000"
    }
  ],
  "subtotal_cents":"6000",
  "discount_cents":"0",
  "taxes":[],
  "total_cents":"6000",
  "payments":[],
  "payment_details":"",
  "notes":"",
  "state":"outstanding",
  "tag_list":["lasagna", "cat"],
  "secure_id":"7hef1rs7p3rm4l1nk",
  "permalink":"https://ACCOUNT_NAME.quadernoapp.com/credit/7hef1rs7p3rm4l1nk",
  "pdf":"https://ACCOUNT_NAME.quadernoapp.com/credit/7hef1rs7p3rm4l1nk.pdf",
  "custom_metadata":{},
  "verification_code": "08V9SBg49G",
  "verification_permalink": "https://example.com/verification/08V9SBg49G"
}

HTTP Request

PUT /credits/CREDIT_ID

In our goal to make Quaderno compliant with tax rules worldwide, the edition of credit notes is limited to: po_number, tag_list, payment_details, notes, custom_metadata and all those related to the billing address: street_line_1,street_line_2,city,region, and postal_code.

Parameters

Parameter Type Description
CREDIT_ID integer The ID of the credit to update. Required
contact object The data of the customer who was refunded.
street_line_1 string The customer's billing address line 1 (Street address/PO Box).
street_line_2 string The customer's billing address line 2 (Apartment/Suite/Unit/Building).
city string The customer's billing city.
region string The customer's billing state/province/region.
postal_code string The customer's billing ZIP or postal code. Available for updates.
payment_processor string The name of the payment processor used to refund the payment.
payment_processor_id string The ID of the transaction in the payment_processor.
po_number string The number of the related order.
tag_list string Multiple tags should be separated by commas
payment_details string Detailed information about the accepted payment methods.
notes string Extra notes about the credit.
custom_metadata hash Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. You can have up to 20 keys, with key names up to 40 characters long and values up to 500 characters long.
verification_code string The code needed to validate the credit note, usually generated by tax authorities.
verification_permalink string The link needed to validate the credit note, usually generated by tax authorities.

Void a credit

As invoices cannot be deleted, you can "convert" an invoice into a credit note with POST /credits which takes the invoice id as parameter. In case you also need to correct this, mark the associated credit note as void using this endpoint.

curl https://ACCOUNT_NAME.quadernoapp.com/api/credits/CREDIT_ID/void \
  -u YOUR_API_KEY:x \
  -X PUT

This returns the whole credit. Note that it will contain a "related_document": { "id": 2005801, "type": "Invoice"}, and a "state": "void",.

HTTP Request

PUT /credits/CREDIT_ID/void

Parameters

Parameter Type Description
CREDIT_ID integer The ID of the credit to void. Required

Deliver a credit

Use this endpoint to send a credit note to your customer.

Emails will be sent from notifications@quaderno.io. If your customer replies to an invoice email, the reply will go to the email address you've set up on the business data page.

curl https://ACCOUNT_NAME.quadernoapp.com/api/credits/CREDIT_ID/deliver \
  -u YOUR_API_KEY:x

HTTP Request

GET /credits/CREDIT_ID/deliver

Parameters

Parameter Type Description
CREDIT_ID integer The ID of the credit to deliver. Required

List all credits

curl https://ACCOUNT_NAME.quadernoapp.com/api/credits \
  -u YOUR_API_KEY:x
[
  {
    "id":"507693322f412e0e2e00000f",
    "number":"0000047",
    "issue_date":"2012-10-11",
    "created_at":"1325376000",
    "related_document":{
      "id":999999,
      "type":"Invoice"
    },
    "contact":{
      "id":"5073f9c22f412e02d0000032",
      "full_name":"Garfield"
    },
    "country":"US",
    "street_line_1":"Street 23",
    "street_line_2":"",
    "city":"New York",
    "region":"New York",
    "postal_code":"10203",
    "po_number":null,
    "due_date":null,
    "currency":"USD",
    "exchange_rate":"0.680309",
    "items":[
      {
        "id":"48151623429",
        "description":"lasagna",
        "quantity":"25.0",
        "unit_price_cents":"375",
        "discount_rate":"0.0",
        "tax_1_name":"",
        "tax_1_rate":"",
        "tax_2_name":"",
        "tax_2_rate":"",
        "reference":"Awesome!",
        "subtotal_cents":"9375",
        "discount_cents":"0",
        "gross_amount_cents":"9375"
      }
    ],
    "subtotal_cents":"9375",
    "discount_cents":"0",
    "taxes":[],
    "total_cents":"9375",
    "payments":[
      {
        "id":"50aca7d92f412eda5200002c",
        "date":"2012-11-21",
        "payment_method":"credit_card",
        "payment_processor":"stripe",
        "payment_processor_id":"ch_19yUdh2eZvKYlo2CkFVBOZG7",
        "amount_cents":"9375",
        "url":"https://ACCOUNT_NAME.quadernoapp.com/api/credits/507693322f412e0e2e00000f/payments/50aca7d92f412eda5200002c.json"
      },
    ],
    "payment_details":"Ask Jon",
    "notes":"",
    "state":"paid",
    "tag_list":["lasagna", "cat"],
    "secure_id":"7hef1rs7p3rm4l1nk",
    "permalink":"https://quadernoapp.com/credit/7hef1rs7p3rm4l1nk",
    "pdf":"https://quadernoapp.com/credit/7hef1rs7p3rm4l1nk.pdf",
    "custom_metadata":{},
    "verification_code": "7XShGnyfgo",
    "verification_permalink": "https://example.com/verification/7XShGnyfgo"
  },

  {
    "id":"507693322f412e0e2e0000da",
    "number":"0000047",
    "issue_date":"2012-10-11",
    "created_at":"1325376020",
    "related_document":{
      "id":875978,
      "type":"Invoice"
    },
    "contact":{
      "id":"5073f9c22f412e02d00004cf",
      "full_name":"Teenage Mutant Ninja Turtles"
    },
    "country":"US",
    "street_line_1":"Melrose Ave, Sewer #3",
    "street_line_2":"",
    "city":"New York",
    "region":"New York",
    "postal_code":"",
    "po_number":null,
    "due_date":null,
    "currency":"USD",
    "exchange_rate":"0.680309",
    "items":[
      {
        "id":"481516234291",
        "description":"pizza",
        "quantity":"15.0",
        "unit_price_cents":"6000",
        "discount_rate":"0.0",
        "tax_1_name":"",
        "tax_1_rate":"",
        "tax_2_name":"",
        "tax_2_rate":"",
        "reference":"Even the bad ones taste good!",
        "subtotal_cents":"6000",
        "discount_cents":"0",
        "gross_amount_cents":"6000"
      }
    ],
    "subtotal_cents":"6000",
    "discount_cents":"0",
    "taxes":[],
    "total_cents":"6000",
    "payments":[],
    "payment_details":"",
    "notes":"",
    "state":"outstanding",
    "tag_list":["pizza", "turtles"],
    "secure_id":"7hes3c0ndp3rm4l1nk",
    "permalink":"https://ACCOUNT_NAME.quadernoapp.com/credit/7hes3c0ndp3rm4l1nk",
    "pdf":"https://ACCOUNT_NAME.quadernoapp.com/credit/7hes3c0ndp3rm4l1nk.pdf",
    "custom_metadata":{},
    "verification_code": "7XShGnyfgo",
    "verification_permalink": "https://example.com/verification/7XShGnyfgo"
  }
]

HTTP Request

GET /credits

Parameters

Parameter Type Description
q string A case-sensitive filter on the list based on the credit's number or customer name.
date string A case-sensitive filter on the list based on the credit's issue date. Pass the date range in the url like ?date=DATE1,DATE2. Date range allows these formats: 2019-01-01,2019-12-31 or 2019/01/01,2019/12/31.
state string A case-sensitive filter on the list based on the credit's state. Can be outstanding, late, paid, void and archived.

Expenses

Expenses are all the expenses that you receive from your providers.

Create an expense

curl https://ACCOUNT_NAME.quadernoapp.com/api/expenses \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
    "contact":{
      "id": 1249483
    },
    "items_attributes":[
      {
        "description":"Office supplies",
        "quantity":"30",
        "unit_price":"12.5",
        "tax_1_rate": "20",
        "tax_1_country": "GB",
        "tax_1_name": "VAT",
        "tax_1_transaction_type": "good"
      },
      {
        "description":"Snacks",
        "quantity":"10",
        "total_amount":"1000",
        "tax_1_rate": "20",
        "tax_1_country": "GB",
        "tax_1_name": "VAT",
        "tax_1_transaction_type": "good"
      }
    ],
    "payment_method": "cash"
  }'

The above command returns JSON structured like this:

{
  "id": 2009894,
  "blocked": false,
  "issue_date": "2021-09-27",
  "created_at": 1632729442,
  "contact": { // your provider contact info
    "id": 1249483,
    "full_name": "Susan Sanders"
  },
  "tax_id": null,
  "country": "GB",
  "street_line_1": null,
  "street_line_2": null,
  "city": null,
  "region": null,
  "postal_code": null,
  "po_number": null,
  "currency": "EUR",
  "subject": null,
  "items": [
    {
      "id": 5890331,
      "product_code": null,
      "description": "Office supplies",
      "quantity": "30.0",
      "unit_price_cents": "1250.0",
      "discount_rate": "0.0",
      "tax_1_name": "VAT",
      "tax_1_rate": 20.0,
      "tax_1_country": "GB",
      "tax_1_region": null,
      "tax_1_transaction_type": "good",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice", // safely to ignore as rate is null
      "reference": null,
      "subtotal_cents": "37500.0",
      "discount_cents": "0.0",
      "gross_amount_cents": "37500.0"
    },
    {
      "id": 5890332,
      "product_code": null,
      "description": "Snacks",
      "quantity": "10.0",
      "unit_price_cents": "8333.0",
      "discount_rate": "0.0",
      "tax_1_name": "VAT",
      "tax_1_rate": 20.0,
      "tax_1_country": "GB",
      "tax_1_region": null,
      "tax_1_transaction_type": "good",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice",
      "reference": null,
      "subtotal_cents": "83333.0",
      "discount_cents": "0.0",
      "gross_amount_cents": "83333.0"
    }
  ],
  "subtotal_cents": "120833.0",
  "discount_cents": "0.0",
  "taxes": [ // summary of expense's calculated taxes
    {
      "label": "VAT (20%)",
      "name": "VAT",
      "rate": 20.0,
      "country": "GB",
      "region": null,
      "transaction_type": "good",
      "taxable_base_cents": "120833.0",
      "amount_cents": 24167
    }
  ],
  "total_cents": 145000,
  "payments": [
    {
      "id": 1412382,
      "date": "2021-09-27",
      "payment_method": "cash",
      "amount_cents": 145000,
      "url": "https://ACCOUNT_NAME.quadernoapp.com/api/expenses/2009894/payments/1412382.json"
    }
  ],
  "notes": null,
  "state": "paid",
  "tag_list": [],
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/expenses/2009894.json",
  "custom_metadata": {}
}

HTTP Request

POST /expenses

Parameters

Parameter Type Description
issue_date date The date of the expense's issue – not necessarily the date the products or services were provided.
currency string Three-letter ISO currency code, in uppercase. Defaults to the account's default currency.
contact object The data of the provider.
items_attributes array Array of document items. Required
payment_method string Use this parameter to mark this document as paid in a single request. Can be credit_card, cash, wire_transfer, direct_debit, check, iou, paypal or other
payment_processor string The name of the payment processor used to take the payment.
payment_processor_id string The ID of the transaction in the payment_processor.
po_number string The number of the related order.
tag_list string Multiple tags should be separated by commas
notes string Extra notes about the expense.
attachment object The data of the file to be attached.
custom_metadata hash Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. You can have up to 20 keys, with key names up to 40 characters long and values up to 500 characters long.

Retrieve an expense

curl https://ACCOUNT_NAME.quadernoapp.com/api/expenses/EXPENSE_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "id": 2009894,
  "blocked": false,
  "issue_date": "2021-09-27",
  "created_at": 1632729442,
  "contact": {
    "id": 1249483,
    "full_name": "Susan Sanders"
  },
  "tax_id": null,
  "country": "GB",
  "street_line_1": null,
  "street_line_2": null,
  "city": null,
  "region": null,
  "postal_code": null,
  "po_number": null,
  "currency": "EUR",
  "subject": null,
  "items": [
    {
      "id": 5890331,
      "product_code": null,
      "description": "Office supplies",
      "quantity": "30.0",
      "unit_price_cents": "1250.0",
      "discount_rate": "0.0",
      "tax_1_name": "VAT",
      "tax_1_rate": 20.0,
      "tax_1_country": "GB",
      "tax_1_region": null,
      "tax_1_transaction_type": "good",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice",
      "reference": null,
      "subtotal_cents": "37500.0",
      "discount_cents": "0.0",
      "gross_amount_cents": "37500.0"
    },
    {
      "id": 5890332,
      "product_code": null,
      "description": "Snacks",
      "quantity": "10.0",
      "unit_price_cents": "8333.0",
      "discount_rate": "0.0",
      "tax_1_name": "VAT",
      "tax_1_rate": 20.0,
      "tax_1_country": "GB",
      "tax_1_region": null,
      "tax_1_transaction_type": "good",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice",
      "reference": null,
      "subtotal_cents": "83333.0",
      "discount_cents": "0.0",
      "gross_amount_cents": "83333.0"
    }
  ],
  "subtotal_cents": "120833.0",
  "discount_cents": "0.0",
  "taxes": [
    {
      "label": "VAT (20%)",
      "name": "VAT",
      "rate": 20.0,
      "country": "GB",
      "region": null,
      "transaction_type": "good",
      "taxable_base_cents": "120833.0",
      "amount_cents": 24167
    }
  ],
  "total_cents": 145000,
  "payments": [
    {
      "id": 1412382,
      "date": "2021-09-27",
      "payment_method": "cash",
      "amount_cents": 145000,
      "url": "https://ACCOUNT_NAME.quadernoapp.com/api/expenses/2009894/payments/1412382.json"
    }
  ],
  "notes": null,
  "state": "paid",
  "tag_list": [],
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/expenses/2009894.json",
  "custom_metadata": {}
}

HTTP Request

GET /expenses/EXPENSE_ID

Parameters

Parameter Type Description
EXPENSE_ID integer The ID of the expense to retrieve. Required

Update an expense

curl https://ACCOUNT_NAME.quadernoapp.com/api/expenses/EXPENSE_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  --header 'Content-Type: application/json' \
  --data '{
      "items_attributes":[
        {
          "id":5890402,
          "description": "2 Printer cartridges and consumable batteries."
        }
      ]
    }'

The above command modifies the inner items object (response trimmed for brevity):


"items": [
  {
    "id": 5890402,
    "product_code": null,
    "description": "2 Printer cartridges and consumable batteries.", // updated data
    "quantity": "30.0",
    "unit_price_cents": "1250.0",
    "discount_rate": "0.0",
    "tax_1_name": "VAT",
    "tax_1_rate": 20.0,
    "tax_1_country": "GB",
    "tax_1_region": null,
    "tax_1_transaction_type": "good",
    "tax_2_name": null,
    "tax_2_rate": null,
    "tax_2_country": null,
    "tax_2_region": null,
    "tax_2_transaction_type": "eservice",
    "reference": null,
    "subtotal_cents": "37500.0",
    "discount_cents": "0.0",
    "gross_amount_cents": "37500.0"
  },

Adding an attachment example (base64 trimmed for brevity):

curl https://ACCOUNT_NAME.quadernoapp.com/api/expenses/EXPENSE_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  --header 'Content-Type: application/json' \
  --data '{
      "attachment": {
        "filename": "ticket.png",
        "data": "iVBORw0KGgo … " // do not include any `data:image/png;base64,` suffix on `data`
      }
    }'

The attachment is not returned on the response. A 200 OK response means the file was successfully attached to the expense.

HTTP Request

PUT /expenses/EXPENSE_ID

Parameters

Parameter Type Description
EXPENSE_ID integer The ID of the expense to update. Required
issue_date date The date of the expense's issue - not necessarily the date the products or services were provided.
contact object The data of the provider.
items_attributes array Array of expense items. Required
payment_processor string The name of the payment processor used to take the payment.
payment_processor_id string The ID of the transaction in the payment_processor.
po_number string The number of the related order.
tag_list string Multiple tags should be separated by commas
notes string Extra notes about the expense.
attachment object The data of the file to be attached.
custom_metadata hash Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. You can have up to 20 keys, with key names up to 40 characters long and values up to 500 characters long.

Delete an expense

curl https://ACCOUNT_NAME.quadernoapp.com/api/expenses/EXPENSE_ID \
  -u YOUR_API_KEY:x \
  -X DELETE

HTTP Request

DELETE /expenses/EXPENSE_ID

Parameters

Parameter Type Description
EXPENSE_ID integer The ID of the expense to delete. Required

List all expenses

curl https://ACCOUNT_NAME.quadernoapp.com/api/expenses \
  -u YOUR_API_KEY:x

The above command returns a JSON list of expenses, which you can filter (see parameters):

[
  {
  "id": 2009927,
  "blocked": false,
  "issue_date": "2021-09-27",
  "created_at": 1632735325,
  "contact": {
    "id": 1249483,
    "full_name": "Susan Sanders"
  },
  "tax_id": null,
  "country": "GB",
  "street_line_1": null,
  "street_line_2": null,
  "city": null,
  "region": null,
  "postal_code": null,
  "po_number": null,
  "currency": "EUR",
  "subject": null,
  "items": [
    {
      "id": 5890402,
      "product_code": null,
      "description": "2 Printer cartridges and consumable batteries.",
      "quantity": "30.0",
      "unit_price_cents": "1250.0",
      "discount_rate": "0.0",
      "tax_1_name": "VAT",
      "tax_1_rate": 20.0,
      "tax_1_country": "GB",
      "tax_1_region": null,
      "tax_1_transaction_type": "good",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice",
      "reference": null,
      "subtotal_cents": "37500.0",
      "discount_cents": "0.0",
      "gross_amount_cents": "37500.0"
    },
    {
      "id": 5890403,
      "product_code": null,
      "description": "Snacks",
      "quantity": "10.0",
      "unit_price_cents": "8333.0",
      "discount_rate": "0.0",
      "tax_1_name": "VAT",
      "tax_1_rate": 20.0,
      "tax_1_country": "GB",
      "tax_1_region": null,
      "tax_1_transaction_type": "good",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice",
      "reference": null,
      "subtotal_cents": "83333.0",
      "discount_cents": "0.0",
      "gross_amount_cents": "83333.0"
    }
  ],
  "subtotal_cents": "120833.0",
  "discount_cents": "0.0",
  "taxes": [
    {
      "label": "VAT (20%)",
      "name": "VAT",
      "rate": 20.0,
      "country": "GB",
      "region": null,
      "transaction_type": "good",
      "taxable_base_cents": "120833.0",
      "amount_cents": 24167
    }
  ],
  "total_cents": 145000,
  "payments": [
    {
      "id": 1412412,
      "date": "2021-09-27",
      "payment_method": "cash",
      "amount_cents": 145000,
      "url": "https://ACCOUNT_NAME.quadernoapp.com/api/expenses/2009927/payments/1412412.json"
    }
  ],
  "notes": "Example note",
  "state": "paid",
  "tag_list": [],
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/expenses/2009927.json",
  "custom_metadata": {}
  },
  {  },
  {  }
]

HTTP Request

GET /expenses

Parameters

Parameter Type Description
q string A case-sensitive filter on the list based on the provider's name or the PO number.
date string A case-sensitive filter on the list based on the expense's issue date. Pass the date range in the url like ?date=DATE1,DATE2. Date range allows these formats: 2019-01-01,2019-12-31 or 2019/01/01,2019/12/31.
state string A case-sensitive filter on the list based on the expense's state. Can be outstanding, late, and paid.
contact integer A case-sensitive filter on the list based on the provider's ID.

Recurring Documents

A "recurring" is a special document that periodically renews itself, generating a recurring invoice or expense. You can use recurring documents to automatically send periodic invoices to your customers, or to track recurring business bills.

Create a recurring

curl https://ACCOUNT_NAME.quadernoapp.com/api/recurring \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
    "contact": {
      "id": 1251096
    },
    "items_attributes": [ {
      "product_code": "prod_consulting"
    } ],
    "payment_details": "Payment due upon receipt via wire transfer",
    "notes": "This recurring uses existing contact and products, and relies on defaults so that an invoice will be sent monthly."
  }'

The above command returns JSON structured like this:

{
  "id": 2010192,
  "recurring_document": "invoice",
  "start_date": "2021-10-28",
  "end_date": null,
  "frequency": "monthly",
  "recurring_period": "months",
  "recurring_frequency": 1,
  "contact": {
    "id": 1251096,
    "full_name": "Susan Sanders"
  },
  "po_number": null,
  "due_days": null,
  "currency": "EUR",
  "subject": null,
  "items": [
    {
      "id": 5890845,
      "product_code": "prod_consulting",
      "description": "Consultancy services",
      "quantity": "1.0",
      "unit_price_cents": "50000.0",
      "discount_rate": "0.0",
      "tax_1_name": null,
      "tax_1_rate": null,
      "tax_1_country": null,
      "tax_1_region": null,
      "tax_1_transaction_type": "eservice",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice",
      "reference": "prod_consulting",
      "subtotal_cents": "50000.0",
      "discount_cents": "0.0",
      "gross_amount_cents": "50000.0"
    }
  ],
  "subtotal": "50000.0",
  "discount": "0.0",
  "taxes": [],
  "total_cents": 50000,
  "payment_details": "Payment due upon receipt via wire transfer",
  "notes": "This recurring uses existing contact and products, and relies on defaults so that an invoice will be sent monthly.",
  "state": "active",
  "tag_list": [],
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/recurring/2010192.json",
  "custom_metadata": {}
}

HTTP Request

POST /recurring

Parameters

Parameter Type Description
recurring_document string Can be invoice or expense. Defaults to invoice
start_date date Date of the issue of the first document for this recurring. Defaults to 1 months from today.
end_date date Date of the issue of the last document for this recurring.
frequency string Can be daily, weekly, biweekly, monthly, bimonthly, quarterly, semiyearly, yearly, biyearly. Defaults to monthly.
delivery string Can be send, create, nothing. Defaults to send.
currency string Three-letter ISO currency code, in uppercase. Defaults to the account's default currency.
contact object The data of the customer or the provider.
items_attributes array Array of document items. Required
po_number string The number of the related order.
tag_list string Multiple tags should be separated by commas
payment_details string Detailed information about the accepted payment methods.
notes string Extra notes about the invoice.
custom_metadata hash Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. You can have up to 20 keys, with key names up to 40 characters long and values up to 500 characters long.

Retrieve a recurring

curl https://ACCOUNT_NAME.quadernoapp.com/api/recurring/RECURRING_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "id": 2010192,
  "recurring_document": "invoice",
  "start_date": "2021-10-28",
  "end_date": null,
  "frequency": "monthly",
  "recurring_period": "months",
  "recurring_frequency": 1,
  "contact": {
    "id": 1251096,
    "full_name": "Susan Sanders"
  },
  "po_number": null,
  "due_days": null,
  "currency": "EUR",
  "subject": null,
  "items": [
    {
      "id": 5890845,
      "product_code": "prod_consulting",
      "description": "Consultancy services",
      "quantity": "1.0",
      "unit_price_cents": "50000.0",
      "discount_rate": "0.0",
      "tax_1_name": null,
      "tax_1_rate": null,
      "tax_1_country": null,
      "tax_1_region": null,
      "tax_1_transaction_type": "eservice",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice",
      "reference": "prod_consulting",
      "subtotal_cents": "50000.0",
      "discount_cents": "0.0",
      "gross_amount_cents": "50000.0"
    }
  ],
  "subtotal": "50000.0",
  "discount": "0.0",
  "taxes": [],
  "total_cents": 50000,
  "payment_details": "Payment due upon receipt via wire transfer",
  "notes": "This recurring uses existing contact and products, and relies on defaults so that an invoice will be sent monthly.",
  "state": "active",
  "tag_list": [],
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/recurring/2010192.json",
  "custom_metadata": {}
}

HTTP Request

GET /recurring/RECURRING_ID

Parameters

Parameter Type Description
RECURRING_ID integer The ID of the recurring to retrieve. Required

Update a recurring

This example applies a discount for the next recurring documents.

curl https://ACCOUNT_NAME.quadernoapp.com/api/recurring/RECURRING_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  --header 'Content-Type: application/json' \
  --data '{
      "items_attributes": [
        {
          "id": 5890845,
          "discount_rate": 10
        }
      ]
    }'

The above command returns JSON structured like this:

{
  "id": 2010192,
  "recurring_document": "invoice",
  "start_date": "2021-10-28",
  "end_date": null,
  "frequency": "monthly",
  "recurring_period": "months",
  "recurring_frequency": 1,
  "contact": {
    "id": 1251096,
    "full_name": "Susan Sanders"
  },
  "po_number": null,
  "due_days": null,
  "currency": "EUR",
  "subject": null,
  "items": [
    {
      "id": 5890845,
      "product_code": "prod_consulting",
      "description": "Consultancy services",
      "quantity": "1.0",
      "unit_price_cents": "50000.0",
      "discount_rate": "10.0", // updated data
      "tax_1_name": null,
      "tax_1_rate": null,
      "tax_1_country": null,
      "tax_1_region": null,
      "tax_1_transaction_type": "eservice",
      "tax_2_name": null,
      "tax_2_rate": null,
      "tax_2_country": null,
      "tax_2_region": null,
      "tax_2_transaction_type": "eservice",
      "reference": "prod_consulting",
      "subtotal_cents": "50000.0",
      "discount_cents": "5000.0",
      "gross_amount_cents": "45000.0"
    }
  ],
  "subtotal": "100000.0",
  "discount": "10000.0",
  "taxes": [],
  "total_cents": 90000,
  "payment_details": "Payment due upon receipt via wire transfer",
  "notes": "This recurring uses existing contact and products, and relies on defaults so that an invoice will be sent monthly.",
  "state": "active",
  "tag_list": [],
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/recurring/2010192.json",
  "custom_metadata": {}
}

You can modify all fields and items of the recurring to apply these changes on the next recurring documents. If you need to remove items from the recurring, use the parameter _destroy set to 1 as documented in document items.

HTTP Request

PUT /recurring/RECURRING_ID

Parameters

Parameter Type Description
RECURRING_ID integer The ID of the recurring to retrieve. Required
end_date date Date of the issue of the last document for this recurring.
delivery string Can be send, create, nothing. Defaults to send.
currency string Three-letter ISO currency code, in uppercase. Defaults to the account's default currency.
contact object The data of the customer or the provider.
items_attributes array Array of document items. Required
po_number string The number of the related order.
tag_list string Multiple tags should be separated by commas
payment_details string Detailed information about the accepted payment methods.
notes string Extra notes about the invoice.
custom_metadata hash Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. You can have up to 20 keys, with key names up to 40 characters long and values up to 500 characters long.

Delete a recurring

curl https://ACCOUNT_NAME.quadernoapp.com/api/recurring/RECURRING_ID \
  -u YOUR_API_KEY:x \
  -X DELETE

HTTP Request

DELETE /recurring/RECURRING_ID

Parameters

Parameter Type Description
RECURRING_ID integer The ID of the recurring to delete. Required

List all recurring

curl https://ACCOUNT_NAME.quadernoapp.com/api/recurring \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id": 2010192,
    "recurring_document": "invoice",
    "start_date": "2021-10-28",
    "end_date": null,
    "frequency": "monthly",
    "recurring_period": "months",
    "recurring_frequency": 1,
    "contact": {
      "id": 1251096,
      "full_name": "Susan Sanders"
    },
    "po_number": null,
    "due_days": null,
    "currency": "EUR",
    "subject": null,
    "items": [
      {
        "id": 5890845,
        "product_code": "prod_consulting",
        "description": "Consultancy services",
        "quantity": "1.0",
        "unit_price_cents": "50000.0",
        "discount_rate": "10.0",
        "tax_1_name": null,
        "tax_1_rate": null,
        "tax_1_country": null,
        "tax_1_region": null,
        "tax_1_transaction_type": "eservice",
        "tax_2_name": null,
        "tax_2_rate": null,
        "tax_2_country": null,
        "tax_2_region": null,
        "tax_2_transaction_type": "eservice",
        "reference": "prod_consulting",
        "subtotal_cents": "50000.0",
        "discount_cents": "5000.0",
        "gross_amount_cents": "45000.0"
      }
    ],
    "subtotal": "100000.0",
    "discount": "10000.0",
    "taxes": [],
    "total_cents": 90000,
    "payment_details": "Payment due upon receipt via wire transfer",
    "notes": "This recurring uses existing contact and products, and relies on defaults so that an invoice will be sent monthly.",
    "state": "active",
    "tag_list": [],
    "url": "https://ACCOUNT_NAME.quadernoapp.com/api/recurring/2010192.json",
    "custom_metadata": {}
  },
  {},
  {}
]

HTTP Request

GET /recurring

Parameters

Parameter Type Description
q string A case-sensitive filter on the list based on the recurring's contact or PO number.
date string A case-sensitive filter on the list based on the invoice's issue date. Pass the date range in the url like ?date=DATE1,DATE2. Date range allows these formats: 2019-01-01,2019-12-31 or 2019/01/01,2019/12/31.
state string A case-sensitive filter on the list based on the invoice's state. Can be active and archived.
contact integer A case-sensitive filter on the list based on the contact's ID.

Document Items

Invoices, credits, and expenses are made up of one or more document items, created via an array of items_attributes on the Billing API.

Document items are internally equivalent to Products which use a slighty different API. However, both can be back-referenced via the product_code field.

Each document item has the following properties:

Parameter Type Description
id integer Unique identifier for the object. Available only for updates
description string An arbitrary string attached to the object. Often useful for displaying to users. Required
product_code string Code of an existing product. If present, you don't need to set the description.
quantity decimal Quantity of units for the document item. Defaults to 1.
unit_price decimal The unit price of the item before any discount or tax is applied. Required if total_amount is not passed.
total_amount decimal The total amount to be charged after discounts and taxes. Required if unit_price is not passed.
discount_rate decimal This represents the discount percent out of 100, if applicable.
tax_1_name string The name of the tax, if applicable. Required if tax_1_rate is present
tax_1_rate decimal The rate of the tax, if applicable. Required if tax_1_name is present
tax_1_country string Tax country, if applicable. Defaults to the contact's country
tax_1_region string Tax region, if applicable. Only for US sales tax and Canada GST.
tax_1_transaction_type string Type of transaction. Can be any tax_code. Defaults to the account's default tax class.
tax_2_name string The name of the additional tax, if applicable. Required if tax_2_rate is present
tax_2_rate decimal The rate of the additional tax, if applicable. Required if tax_2_name is present
tax_2_country string Tax country, if applicable. Defaults to the contact's country
tax_2_region string Tax region, if applicable. Only for US sales tax and Canada GST.
tax_2_transaction_type string Type of transaction. Can be any tax_code. Defaults to the account's default tax class.
_destroy integer Set it to 1 if you want to remove the document item selected by ID. Available only for updates.

Attachments

Invoices and expenses can have attached files. Each attachment has the following properties:

Parameter Type Description
id integer Unique identifier for the object. Available only for updates.
data string File data enconded in base64. Required.
filename string Name of the attached file. Required.
public integer Visibility of attached file. Public attachments can be seen and downloaded by customers. Defaults to false.

Payments

Payments in Quaderno-lingo represent the recording of a successful payment.

Like products, payments are a sub-object and can be attached to invoices, expenses and credits. However, payments cannot exist in Quaderno separately to an instance of one of these classes, and the same payment cannot be referenced by more than one instance.

Create a payment

curl https://ACCOUNT_NAME.quadernoapp.com/api/invoices/INVOICE_ID/payments \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
      "amount": 9.99,
      "payment_method": "credit_card" \
      "payment_processor": "stripe" \
      "payment_processor_id": "ch_19yUdxxxx"
    }'

The above command returns JSON structured like this:

{
  "id": 1412133,
  "date": "2021-09-24",
  "payment_method": "credit_card",
  "amount_cents": 999,
  "url": "https://ACCOUNT_NAME.quadernoapp.com/api/invoices/2009439/payments/1412133.json"
}

When trying to register an amount higher than the money pending to be paid on an invoice, you'll get an error like:

{
  "errors": {
    "amount": [
      "must be less than or equal to €109.09" // that import is the money pending to be paid on the invoice
    ]
  }
}

HTTP Request

POST /invoices/INVOICE_ID/payments

POST /expenses/EXPENSE_ID/payments

POST /credits/CREDIT_ID/payments

Parameters

Parameter Type Description
amount decimal Amount paid. Required
date date The date of the payment . Defaults to today.
payment_method string One of the following: credit_card, cash, wire_transfer, direct_debit, check, iou, paypal or other. Required
payment_processor string Payment processor that you used to process the payment.
payment_processor_id string The transaction ID that the payment processor assigned to the payment.

Delete a payment

curl https://ACCOUNT_NAME.quadernoapp.com/api/invoices/INVOICE_ID/payments/PAYMENT_ID \
  -u YOUR_API_KEY:x \
  -X DELETE

HTTP Request

DELETE /invoices/INVOICE_ID/payments/PAYMENT_ID

DELETE /expenses/EXPENSE_ID/payments/PAYMENT_ID

DELETE /credits/CREDIT_ID/payments/PAYMENT_ID

CHECKOUT

Sessions

A Checkout Session represents your customer's session as they pay for one-time purchases or subscriptions through Quaderno Checkout.

Session objects transition through the following statuses: pending, processing, abandoned, failed, and completed. Abandoned sessions are automatically removed after seven days of inactivity.

After you create a Session, you may want to subscribe to its webhook to fulfill the order, learn more about this on this guide.

Create a Session

A Checkout Link is a permanent URL that spans a new Checkout Session for one-time purchases or subscriptions, like a template for creating your own online buying forms. With this API you can programatically create Checkout Sessions on the fly.

curl https://ACCOUNT_NAME.quadernoapp.com/api/checkout/sessions \
  -u YOUR_API_KEY:x \
  -X POST \
  --header 'Content-Type: application/json' \
  --data '{
      "payment_methods": ["card"],
      "success_url": "https://example.com/success",
      "items": [{
         "product": "9969",
         "amount": 100.0,
         "name": "A product",
         "description": null,
         "currency": "USD",
         "quantity": 1
      }],
      "cancel_url": "https://example.com/canceled",
      "custom": {
         "stripe": {
            "metadata": {
               "notify_url": "xxx",
               "user_id": 999
            }
         }
      },
      "billing_details_collection": "auto",
      "customer": {
         "billing_city": "New York",
         "billing_country": "US",
         "billing_postal_code": "10128",
         "billing_street_line_1": "E 90th Street",
         "email": "anemail@example.com",
         "first_name": "Susan",
         "last_name": "Sanders",
         "tax_id": "123456789"
      }
   }'

The above command returns JSON structured like this:

{
  "id": 12901,
  "status": "pending",
  "billing_details_collection": "auto",
  "cancel_url": "https://example.com/canceled",
  "coupon_collection": false,
  "locale": "auto",
  "payment_methods": [
    "card"
  ],
  "success_url": "https://example.com/success",
  "custom": {
    "stripe": {
      "metadata": {
        "notify_url": "xxx",
        "user_id": "999"
      }
    }
  },
  "metadata": {},
  "processor": null,
  "processor_id": null,
  "items": [
    {
      "product": "9969",
      "amount": 100.0,
      "name": "A product",
      "description": null,
      "currency": "USD",
      "quantity": 1
    }
  ],
  "customer": {
    "billing_city": "New York",
    "billing_country": "US",
    "billing_postal_code": "10128",
    "billing_street_line_1": "E 90th Street",
    "billing_street_line_2": null,
    "company": null,
    "email": "anemail@example.com",
    "first_name": "Susan",
    "last_name": "Sanders",
    "tax_id": "123456789",
    "business_number": null
  },
  "permalink": "https://ACCOUNT_NAME.quadernoapp.com/checkout/session/xxxx"
}

HTTP Request

POST /checkout/sessions

Parameters

Parameter Type Description
billing_details_collection string The value for whether Checkout collected the customer’s billing details. Values are auto and required (default to required).
cancel_url storing The URL the customer will be redirected to if they decide to cancel the payment and return to your website. Required
coupon_collection boolean The value for whether Checkout collected coupons.
custom object Set of key-value pairs that you want to forward to the payment processor. This can be useful for setting up additional options in the payment processor. If you want to send specific data to one processor, you can create a subhash with the name of that particular processor. E.g.: stripe: { metadata: { user_id: 999 } }, paypal: { no_shipping: 0 }
customer object The customer's billing information. Check the attributes here.
items array The list of products purchased by the customer. Check the attributes here. Required
locale string The 2-letter ISO code of the language the Checkout is displayed in. Values are auto, ca, de, en, es, fi, fr, hu, nl, sv, and no.
metadata object Set of key-value pairs that you can attach to the session. This can be useful for storing additional information about the purchase.
payment_methods array Can be card or paypal.
success_url string The URL the customer will be redirected to after the payment is successful. Required


Here are the parameters for the Session's children objects:

Customer object parameters:
Parameter Type Description
billing_city string City/District/Suburb/Town/Village. Use this parameter to prefill customer data if you already have her city on file.
billing_country string 2-letter country code. Use this parameter to prefill customer data if you already have her country on file.
billing_postal_code string ZIP or postal code. Use this parameter to prefill customer data if you already have her postal code on file.
billing_street_line_1 string Address line 1 (Street address/PO Box). Use this parameter to prefill customer data if you already have her street address on file.
billing_street_line_2 string Address line 2 (Apartment/Suite/Unit/Building). Use this parameter to prefill customer data if you already have her street address on file.
contact integer The ID of the Quaderno Contact. A new contact will be created unless an existing contact was provided in when the Checkout was created.
company string Customer company. Use this parameter to prefill customer data if you already have her company name on file.
email string Customer email. Use this parameter to prefill customer data if you already have an email on file.
first_name string Customer first name. Use this parameter to prefill customer data if you already have her first name on file.
last_name string Customer last name. Use this parameter to prefill customer data if you already have her last name on file
tax_id string Customer Tax ID. Use this parameter to prefill customer data if you already have a tax id on file.
Items object parameters:
Parameter Type Description
amount number(decimal) Unit price of the item being purchased. Defaults to the product's price is used if amount is not set.
currency string Three-letter ISO currency code, in uppercase. Must be a supported currency by the payment processor. Defaults to the product's currency is used if currency is not set.
description string The description of the item being purchased. Defaults to the product's description is used if description is not set.
name string The name of the item being purchased. Defaults to the product's name is used if name is not set.
product string The SKU of the Quaderno Product (code parameter from /items ). You need to create it first or use an existing one, or you'll get a {"errors": {"items": ["Product code xxx is not defined"]}} error. Required
quantity integer Quantity of the item being purchased. Minimum value is 1.


Retrieve a session

Retrieves the details of an existing session. You only need to supply the unique session identifier that was returned upon session creation.

curl https://ACCOUNT_NAME.quadernoapp.com/api/checkout/sessions/SESSION_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
      "id":1,
      "status":"completed",
      "billing_details_collection":"required",
      "cancel_url":"http://go.back.com",
      "coupon_collection":true,
      "locale":"auto",
      "payment_methods":["card", "paypal"],
      "success_url":"http://success.com?prod=1",
      "custom":{},
      "items":[
         {
            "product":"prod_61ffa845b4a0b8",
            "amount":999.0,
            "name":"A test item",
            "description":"This a test item.",
            "currency":"EUR",
            "quantity":1
         }
      ],
      "customer":{
         "billing_city":"London",
         "billing_country":"GB",
         "billing_postal_code":"E1 6AN",
         "billing_street_line_1":"A street name",
         "billing_street_line_2":"",
         "company":"",
         "email":"john@doe.com",
         "first_name":"John",
         "last_name":"Doe",
         "tax_id":null
      },
      "metadata": {},
      "permalink":"https://ACCOUNT_NAME.quadernoapp.com/checkout/session/xxx"
   }

HTTP Request

GET /checkout/sessions/SESSION_ID

Parameters

Parameter Type Description
SESSION_ID integer The ID of the session to retrieve. Required

Update a session

Updates the specified session by setting new values for the parameters passed. Any parameters not provided will be left unchanged. Only pending sessions can be updated.

This request accepts mostly the same arguments as the session creation call, please refer to it for checking the accepted parameters.

curl https://ACCOUNT_NAME.quadernoapp.com/api/checkout/sessions/SESSION_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  --header 'Content-Type: application/json' \
  --data '{
      "success_url": "https://example.com/new_success",
      "metadata": {
         "your_custom_metadata": "xxx"
      }
   }'

The above command returns JSON structured like this:

{
  "id": 12901,
  "status": "pending",
  "billing_details_collection": "auto",
  "cancel_url": "https://example.com/canceled",
  "coupon_collection": false,
  "locale": "auto",
  "payment_methods": [
    "card"
  ],
  "success_url": "https://example.com/new_success", // updated data included in the request
  "custom": {
    "stripe": {
      "metadata": {
        "notify_url": "xxx",
        "user_id": "999"
      }
    }
  },
  "metadata": { // new data included in the request
    "your_custom_metadata": "xxx"
  },
  "processor": null,
  "processor_id": null,
  "items": [
    {
      "product": "9969",
      "amount": 100.0,
      "name": "A product",
      "description": null,
      "currency": "USD",
      "quantity": 1
    }
  ],
  "customer": {
    "billing_city": "New York",
    "billing_country": "US",
    "billing_postal_code": "10128",
    "billing_street_line_1": "E 90th Street",
    "billing_street_line_2": null,
    "company": null,
    "email": "anemail@example.com",
    "first_name": "Susan",
    "last_name": "Sanders",
    "tax_id": "123456789",
    "business_number": null
  },
  "permalink": "https://ACCOUNT_NAME.quadernoapp.com/checkout/session/xxxx"
}

HTTP Request

PUT /checkout/sessions/SESSION_ID

Parameters

Please refer to Create a Session for checking the accepted parameters.

Delete a session

Permanently deletes a session. It cannot be undone. Only pending sessions can be deleted.

$ curl  https://quadernoapp.com/api/checkout/sessions/SESSION_ID \
  -u YOUR_API_KEY:x \
  -X DELETE 

HTTP Request

DELETE /checkout/sessions/SESSION_ID

Parameters

Parameter Type Description
SESSION_ID integer The ID of the session to delete. Required


List all sessions

curl https://ACCOUNT_NAME.quadernoapp.com/api/checkout/sessions \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id": 1,
    "status": "completed",
    "billing_details_collection": "required",
    "cancel_url": "https://example.com/canceled",
    "coupon_collection": false,
    "locale": "auto",
    "payment_methods": [
      "card"
    ],
    "success_url": "https://example.com/success",
    "custom": {},
    "metadata": {},
    "processor": "stripe",
    "processor_id": "pi_xxxx",
    "items": [
      {
        "product": "prod_226500ed1edc5c",
        "amount": 10000.0,
        "name": "A digital service",
        "description": null,
        "currency": "USD",
        "quantity": 1
      }
    ],
    "customer": {
      "billing_city": "New York",
      "billing_country": "US",
      "billing_postal_code": "10128",
      "billing_street_line_1": "E 90th Street",
      "billing_street_line_2": "",
      "company": "",
      "email": "anemail@example.com",
      "first_name": "Susan",
      "last_name": "Sanders",
      "tax_id": "123456789",
      "business_number": null
    },
    "permalink": "https://ACCOUNT_NAME.quadernoapp.com/checkout/session/xxxx"
  }
]

You can list all sessions, or list the sessions for a specific status. The sessions are returned sorted by creation date, with the most recently created sessions appearing first.

Returns an array of sessions. Each entry in the array is a separate session object. If no more sessions are available, the resulting array will be empty.

HTTP Request

GET /checkout/sessions

Parameters

Parameter Type Description
status string A case-sensitive filter on the list based on the session's status. Can be pending, processing, abandoned, failed, and completed.

Coupons

A coupon contains information about a percent-off or amount-off discount you might want to apply to a customer during a checkout.

Create a coupon

curl https://ACCOUNT_NAME.quadernoapp.com/api/checkout/coupons \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
      "code": "SALES",
      "name": "",
      "percent_off": 20,
      "max_redemptions": 100,
      "redeem_by": "2021-11-01"
  }'

The above command returns JSON structured like this:

{
  "id": 2,
  "code": "SALES",
  "name": "",
  "percent_off": 20,
  "times_redeemed": null,
  "processor_id": "SALES",
  "amount_off_cents": null,
  "currency": null,
  "max_redemptions": 100,
  "redeem_by": "2021-11-01"
}
curl https://ACCOUNT_NAME.quadernoapp.com/api/checkout/coupons \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
      "code": "FINAL",
      "amount_off": 10,
      "currency": "EUR",
      "max_redemptions": 10,
      "redeem_by": "2021-12-01"
  }'

The above command returns JSON structured like this:

{
  "id": 3,
  "code": "FINAL",
  "name": null,
  "percent_off": null,
  "times_redeemed": null,
  "processor_id": "FINAL",
  "amount_off_cents": 1000,
  "currency": "EUR",
  "max_redemptions": 10,
  "redeem_by": "2021-12-01"
}

HTTP Request

POST /checkout/coupons

Parameters

Parameter Type Description
code string The coupon's code. Required
name string Name of the coupon displayed to customers on invoices.
percent_off decimal Percent that will be taken off the subtotal of the purchase. For example, a coupon with percent_off of 50 will make a $100 purchase $50 instead. Required if amount_off is not passed.
amount_off decimal Amount (in the currency specified) that will be taken off the subtotal of the purchase. Required if percent_off is not passed.
currency string If amount_off has been set, the three-letter ISO currency code of the amount to take off, in uppercase. Must be a supported currency in your payment processors.
redeem_by date Date after which the coupon can no longer be redeemed.
max_redemptions integer Maximum number of times this coupon can be redeemed, in total, across all customers, before it is no longer valid.

Retrieve a coupon

curl https://ACCOUNT_NAME.quadernoapp.com/api/checkout/coupons/COUPON_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "id": 8888,
  "code": "awesome",
  "name": "Awesome discount",
  "percent_off": 50,
  "times_redeemed": null,
  "processor_id": "awesome01",
  "amount_off_cents": null,
  "currency": null,
  "max_redemptions": null,
  "redeem_by": null
}

HTTP Request

GET /checkout/coupons/COUPON_ID

Parameters

Parameter Type Description
COUPON_ID integer The ID of the coupon to retrieve. Required

Update a coupon

curl https://ACCOUNT_NAME.quadernoapp.com/api/checkout/coupons/COUPON_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  -d '{ "percent_off": 25 }'

The above command returns JSON structured like this:

{
  "id": 8888,
  "code": "awesome",
  "name": "Awesome discount",
  "percent_off": 25, // updated field
  "times_redeemed": null,
  "processor_id": "awesome01",
  "amount_off_cents": null,
  "currency": null,
  "max_redemptions": null,
  "redeem_by": null
}

Note that if you're using our Stripe integration, coupons are linked to Stripe coupons. Coupon edition won't be allowed as Stripe doesn't allow editions to their coupons.

HTTP Request

PUT /checkout/coupons/COUPON_ID

Parameters

Parameter Type Description
COUPON_ID integer The ID of the coupon to update. Required
code string The coupon's code. Required
name string Name of the coupon displayed to customers on invoices.
percent_off decimal Percent that will be taken off the subtotal of the purchase. For example, a coupon with percent_off of 50 will make a $100 purchase $50 instead. Required if amount_off is not passed.
amount_off decimal Amount (in the currency specified) that will be taken off the subtotal of the purchase. Required if percent_off is not passed.
currency string If amount_off has been set, the three-letter ISO currency code of the amount to take off, in uppercase. Must be a supported currency in your payment processors.
redeem_by date Date after which the coupon can no longer be redeemed.
max_redemptions integer Maximum number of times this coupon can be redeemed, in total, across all customers, before it is no longer valid.

Delete a coupon

curl https://ACCOUNT_NAME.quadernoapp.com/api/checkout/coupons/COUPON_ID \
  -u YOUR_API_KEY:x \
  -X DELETE

HTTP Request

DELETE /checkout/coupons/COUPON_ID

Parameters

Parameter Type Description
COUPON_ID integer The ID of the coupon to delete. Required

List all coupons

GET /checkout/coupons

curl https://ACCOUNT_NAME.quadernoapp.com/api/checkout/coupons \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id": 1,
    "code": "FALL21",
    "name": "",
    "percent_off": 20,
    "times_redeemed": null,
    "processor_id": "FALL21",
    "amount_off_cents": null,
    "currency": null,
    "max_redemptions": 100,
    "redeem_by": "2021-11-01"
  },
  {
    "id": 2,
    "code": "SALES",
    "name": "",
    "percent_off": 20,
    "times_redeemed": null,
    "processor_id": "SALES",
    "amount_off_cents": null,
    "currency": null,
    "max_redemptions": 100,
    "redeem_by": "2021-11-01"
  },
  {
    "id": 3,
    "code": "FINAL",
    "name": null,
    "percent_off": null,
    "times_redeemed": null,
    "processor_id": "FINAL",
    "amount_off_cents": 1000,
    "currency": "EUR",
    "max_redemptions": 10,
    "redeem_by": "2021-12-01"
  }
]

GETting from /checkout/coupons will return all the account's coupons.

CONNECT

Are you a marketplace platform? Quaderno Connect provides tax management and invoicing capabilities to your business model! Go check this step-by-step guide for details on using Quaderno Apps with Quaderno Connect Standard accounts.

Accounts (beta)

When using Quaderno Connect, you need to create an account (known as a connected account) for each user that needs to calculate taxes on your platform. These accounts are generally created when a user signs up for your platform.

Create an account

curl --request POST \
  --url https://ACCOUNT_NAME.quadernoapp.com/api/accounts/ \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
    "business_name": "Big Bang Inc.",
    "email": "bigbang@example.com",
    "country": "US",
    "postal_code": "10001",
    "local_tax_id": "123456789",
    "default_product_type": "service",
    "default_tax_code": "ebook"
  }'

The above command returns JSON structured like this:

{
  "id": 21791,
  "authentication": { // to be used to act on behalf of the new account
    "token_type": "Bearer",
    "access_token": "xxx", // expires on 25 days
    "refresh_token": "yyy" // does not expire on Custom accounts
  },
  "business_name": "Big Bang Inc.",
  "country": "US",
  "created_at": 1632212259,
  "currency": "USD",
  "default_product_type": "service",
  "default_tax_code": "ebook",
  "email": "bigbang@example.com",
  "local_tax_id": "123456789",
  "state": "active",
  "subdomain": "ninive-xxxx", // the new ACCOUNT_NAME for your merchant
  "type": "custom"
}

HTTP Request

POST /accounts

Parameters

Parameter Type Description
business_name string The account's business name. Required
email string The account's email address. Required
country string The account's country. 2-letter country code. Required
postal_code string The account's postal code. Recommended whether the account is based in the US or Canada to determine the state/province.
currency string The account's accounting currency. Defaults to the country's official currency.
local_tax_id string The account's tax ID. If set, the account will be registered for tax collection in the country's jurisdiction.
default_product_type string The account's default product type to be used when no product type is specified on documents and tax calculations. Can be either good or service. Defaults to service.
default_tax_code string The account's default product type to be used when no tax code is specified on items and tax calculations. Can be any tax_code. Defaults to eservice.

Retrieve an account

curl https://ACCOUNT_NAME.quadernoapp.com/api/accounts/ACCOUNT_ID \
  -u YOUR_API_KEY:x \

The above command returns JSON structured like this:

  {
    "id": 6,
    "business_name": "Big Bang Inc.",
    "country": "US",
    "created_at": 1632213997,
    "currency": "USD",
    "default_product_type": "service",
    "default_tax_code": "ebook",
    "email": "bigbang@example.com",
    "local_tax_id": null,
    "state": "active",
    "subdomain": "assur-8680",
    "type": "custom"
  }

HTTP Request

GET /accounts/ACCOUNT_ID

Parameters

Parameter Type Description
ACCOUNT_ID integer The ID of the account to retrieve. Required

Update an account

curl https://ACCOUNT_NAME.quadernoapp.com/api/accounts/ACCOUNT_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  -d '{ "business_name": "Another Name Inc." }'

The above command returns JSON structured like this:

  {
    "id": 6,
    "business_name": "Another Name Inc.",
    "country": "US",
    "created_at": 1632213997,
    "currency": "USD",
    "default_product_type": "service",
    "default_tax_code": "ebook",
    "email": "bigbang@example.com",
    "local_tax_id": null,
    "state": "active",
    "subdomain": "assur-8680",
    "type": "custom"
  }

HTTP Request

PUT /accounts/ACCOUNT_ID

Parameters

Parameter Type Description
ACCOUNT_ID integer The ID of the account to retrieve. Required
business_name string The accounts's business name.
email string The account's email address.
default_product_type string The account's default product type to be used when no product type is specified on documents and tax calculations. Can be either good or service. Defaults to service.
default_tax_code string The account's default product type to be used when no tax code is specified on items and tax calculations. Can be any tax_code. Defaults to eservice.

List all accounts

curl https://ACCOUNT_NAME.quadernoapp.com/api/accounts \
  -u YOUR_API_KEY:x

The above command returns a JSON list structured like this:

[
  {
    "id": 6,
    "business_name": "Big Bang Inc.",
    "country": "US",
    "created_at": 1632213997,
    "currency": "USD",
    "default_product_type": "service",
    "default_tax_code": "ebook",
    "email": "bigbang@example.com",
    "local_tax_id": null,
    "state": "active",
    "subdomain": "assur-8680",
    "type": "custom"
  },
  
]

HTTP Request

GET /accounts

TRANSACTIONS

Create a Transaction object to easily send sales & refunds from your platform to Quaderno. We'll use them to issue invoices & credit notes, as well as update tax reports automatically.

The Transactions API is perfect for use cases where your customers pay you in real time. It can be used together with Quaderno Connect. With Transactions API, you'll be able to issue the invoice with the payment information and create the contact or reference the existing contact in just one performant API call.

Create a sale transaction

curl https://ACCOUNT_NAME.quadernoapp.com/api/transactions \
  -u YOUR_API_KEY:x \
  -X POST \
  --header 'Content-Type: application/json' \
  --data '{
    "type": "sale",
    "customer": {
      "first_name": "John", 
      "last_name": "Smith",
      "email": "an_email@example.com"
    },
    "evidence": {
      "billing_country": "US",
      "ip_address": "255.255.255.255",
      "bank_country": "US"
    },
    "currency": "USD",
    "items": [
      {
        "description": "Awesome ebook",
        "quantity": 1,
        "amount": 19,
        "tax": {
          "city": null,
          "country": "US",
          "county": null,
          "currency": "USD",
          "name": null,
          "notes": null,
          "notice": "",
          "product_type": "service",
          "rate": 0.0,
          "region": "NY",
          "tax_behavior": "exclusive",
          "tax_code": "ebook",
          "taxable_part": null,
          "import": true,
          "subtotal": null,
          "tax_amount": null,
          "total_amount": null
        }
      },
      {
        "description": "Awesome SaaS",
        "quantity": 1,
        "amount": 89,
        "tax": {
          "city": "NEW YORK",
          "country": "US",
          "county": "NEW YORK",
          "currency": "USD",
          "name": "Sales tax",
          "notes": null,
          "notice": "",
          "product_type": "service",
          "rate": 8.875,
          "region": "NY",
          "tax_behavior": "exclusive",
          "tax_code": "saas",
          "taxable_part": 100.0,
          "import": true,
          "subtotal": null,
          "tax_amount": null,
          "total_amount": null
        }
      }
    ], 
    "payment": {
      "method": "credit_card",
      "processor": "aPaymentProcessor",
      "processor_id": "ch_xxxxx"
    },
    "processor": "your-user-agent",
    "processor_id": "your-internal-request-id",
    "po_number": "PO_xxxxxx",
    "tags": "tag-a,tag-b,tag-c",
    "custom_metadata": {
      "anything_you_want": "extra info"
    }
  }'

The above command returns JSON structured like this:

{
  "id": 2008034,
  "contact": {
    "id": 1248822,
    "city": null,
    "country": "ES",
    "created_at": 1631884113,
    "email": "an_email@example.com",
    "first_name": "John",
    "full_name": "John Smith",
    "kind": "person",
    "language": "ES",
    "last_name": "Smith",
    "notes": null,
    "permalink": "https://ACCOUNT_NAME.quadernoapp.com/billing/th3p3rm4l1nk?otp=xxx",
    "phone_1": null,
    "postal_code": null,
    "processor": null,
    "processor_id": null,
    "region": null,
    "street_line_1": null,
    "street_line_2": null,
    "tax_id": null,
    "web": null
  },
  "created_at": 1631884142,
  "currency": "USD",
  "custom_metadata": {
    "anything_you_want": "extra info"
  },
  "discount_cents": 0,
  "evidence": {
    "bank_country": "US",
    "billing_country": "US",
    "created_at": 1631884142,
    "ip_address": "255.255.255.255",
    "ip_country": null,
    "state": "confirmed"
  },
  "issue_date": "2021-09-17",
  "items": [
    {
      "id": 5888097,
      "created_at": 1631884142,
      "description": "Awesome ebook",
      "discount_cents": 0,
      "discount_rate": 0.0,
      "product_code": null,
      "quantity": 1.0,
      "subtotal_cents": 1900,
      "total_amount_cents": 1900,
      "unit_price": 19.0
    },
    {
      "id": 5888098,
      "created_at": 1631884142,
      "description": "Awesome SaaS",
      "discount_cents": 0,
      "discount_rate": 0.0,
      "product_code": null,
      "quantity": 1.0,
      "subtotal_cents": 8175,
      "tax_code": "saas",
      "tax_country": "US",
      "tax_name": "Sales tax",
      "tax_rate": 8.875,
      "tax_region": "NY",
      "taxable_part": "100.0",
      "total_amount_cents": 8900,
      "unit_price": 81.75
    }
  ],
  "notes": null,
  "number": "00019",
  "payments": [
    {
      "id": 1410566,
      "amount_cents": 10800,
      "created_at": 1631884142,
      "date": "2021-09-17",
      "payment_method": "credit_card",
      "processor": "aPaymentProcessor", // Payment processor
      "processor_id": "ch_xxxxx"
    }
  ],
  "pdf": "https://quadernoapp.com/invoice/th3p3rm4l1nk.pdf?otp=xxx&q=yyy",
  "permalink": "https://quadernoapp.com/invoice/th3p3rm4l1nk?otp=xxx&q=yyy",
  "po_number": "PO_xxxxxx",
  "processor": "my_api_user_agent", // Transaction processor (your user-agent)
  "processor_id": "id_to_reference_transaction", // reference this when creating a type: refund
  "state": "outstanding",
  "subtotal_cents": 10075,
  "tags": "tag-a, tag-b, tag-c",
  "taxes": [
    {
      "amount_cents": 725,
      "country": "US",
      "label": "Sales tax (8.875%)",
      "rate": 8.875,
      "region": "NY",
      "tax_code": "saas"
    }
  ],
  "total_cents": 10800,
  "type": "Invoice"
}

HTTP Request

POST /transactions

Parameters

Transaction parameters:

Parameter Type Description
type string The transaction's type. Can be sale and refund. Defaults to sale.
date date The transaction's date. Defaults to today.
customer object The data of the customer who paid the transaction. You can reference an existing contact by its id, or pass the json object to create a new contact.
shipping_address object The mailing address to where the order will be shipped. Use it if the order contains physical goods.
currency string Three-letter ISO currency code, in uppercase. Defaults to the account's default currency.
items array The individual items that make up the transaction. See parameters below. Required
payment object Detailed information about the transaction payment. See parameters below. Required
processor string The name of the platform that processed the transaction. E.g. shopify, woocommerce or any user agent you may want to use to identify yourself… Recommended
processor_id string The ID of the transaction in the processor. Reference this when creating a transaction of type: refund, so that the resultant credit note is associated to the invoice. Recommended
evidence object Location evidence of the customer's residence. See parameters below. Highly recommended
po_number string The number of the related order. Recommended
tags string Tags attached to the transaction, formatted as a string of comma-separated values. Tags are additional short descriptors, commonly used for filtering and searching. Each individual tag is limited to 40 characters in length.
notes string Optional notes attached to the transaction.
custom_metadata hash Set of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format. You can have up to 20 keys, with key names up to 40 characters long and values up to 500 characters long.

Transaction item parameters:

Parameter Type Description
product_code string Code of an existing product. If present, you don't need to set the description.
description string An arbitrary string attached to the object. Often useful for displaying to users. Required.
quantity integer Quantity of units for the transaction item. Defaults to 1.
discount_rate integer This represents the discount percent out of 100 included in the amount, if applicable.
tax object The tax rates which apply to the transaction item. Use the response from the tax calculation endpoint.
amount decimal Total amount (in the currency specified) of the transaction item. This should always be equal to the amount charged after discounts and taxes. Required.

Transaction payment parameters:

Parameter Type Description
method string The payment method used to pay the transaction. Can be credit_card, cash, wire_transfer, direct_debit, check, iou, paypal or other Defaults to other.
processor string The name of the payment processor used to take the payment. E.g. stripe, paypal…
processor_id string The ID of the transaction in the processor.

Transaction evidence parameters:

Parameter Type Description
billing_country string Customer's billing country (2-letter ISO code)
ip_address string Customer's IP address
bank_country string Customer's bank country (2-letter ISO code)

Create a refund transaction

To create a refund using the Transactions API, please use type: refund plus the same processor_id that you used when registering your sale.

REPORTING

Report Requests

Every time you need to generate a Quaderno report, you need to create a report request with specific parameters. When the report has finished running, GETting the request id will give you the reference to the file where you can retrieve your results.

Create a request

curl https://ACCOUNT_NAME.quadernoapp.com/api/reporting/requests \
  -u YOUR_API_KEY:x \
  --header 'Content-Type: application/json' \
  --data '{
    "report_type": "tax_summary",
    "parameters": {
      "from_date": "2021-09-01",
      "to_date": "2021-09-30"
    }
  }'

The above command returns JSON structured like this:

{
  "id": 8, // use the request id to get the final file later
  "created_at": 1632988001,
  "parameters": {
    "from_date": "2021-09-01",
    "to_date": "2021-09-30"
  },
  "report_type": "tax_summary",
  "report_url": null, // always null at first
  "state": "pending"
}

HTTP Request

POST /reporting/requests

Parameters

Parameter Type Description
report_type string The type of report. Can be tax_summary, invoices_list or credits_list. Required
parameters string Parameters specifying how the report should be run. Different report types have different required and optional parameters.

Parameters for tax summary, invoices list, and credits list:

Parameter Type Description
from_date date Starting date of data to be included in the report. Required
to_date date Ending date of data to be included in the report. Required

Retrieve a request

After a while from creating the report request, the file will be ready. Use the request id to get it.

curl https://ACCOUNT_NAME.quadernoapp.com/api/reporting/requests/REQUEST_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
  "id": 41,
  "completed_at": 1633519917,
  "created_at": 1633519917,
  "parameters": {
    "from_date": "2021-09-01",
    "to_date": "2021-09-30"
  },
  "report_type": "tax_summary",
  "report_url": "https://quaderno.s3.amazonaws.com/reports/41/xxxxxxx",
  "state": "succeeded"
}

HTTP Request

GET /reporting/requests/REQUEST_ID

Parameters

Parameter Type Description
REQUEST_ID integer The ID of the request to retrieve. Required

List all requests

curl https://ACCOUNT_NAME.quadernoapp.com/api/reporting/requests \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id": 1,
    "completed_at": 1612351337,
    "created_at": 1612351337,
    "parameters": {
      "from_date": "2020-10-01",
      "to_date": "2020-12-31"
    },
    "report_type": "tax_summary",
    "report_url": "https://quaderno.s3.amazonaws.com/reports/xxxxxxxxx",
    "state": "succeeded"
  },
  {
    "id": 32,
    "created_at": 1632993561,
    "error": "invalid date", // this report failed because september has 30 days, not 31
    "parameters": {
      "from_date": "2021-09-01",
      "to_date": "2021-09-31"
    },
    "report_type": "tax_summary",
    "report_url": null,
    "state": "failed"
  }
]

HTTP Request

GET /reporting/requests

WEBHOOKS

Webhooks Endpoints

Webhooks refers to a combination of elements that collectively create a notification and reaction system within a larger integration.

Metaphorically, webhooks are like a phone number that Quaderno calls to notify you of activity in your Quaderno account. The activity could be the creation of a new customer or the payment of an invoice. The webhook endpoint is the person answering that call who takes actions based upon the specific information it receives.

Non-metaphorically, the webhook endpoint has an associated URL (e.g., https://example.com/webhooks) and is just more code on your server, which could be written in Ruby, PHP, Node.js, or whatever.

The Quaderno notifications are Event objects. This Event object contains all the relevant information about what just happened, including the type of event and the data associated with that event. The webhook endpoint uses the event details to take any required actions, such as indicating that an order should be fulfilled.

Create a webhook

curl https://ACCOUNT_NAME.quadernoapp.com/api/webhooks \
  -u YOUR_API_KEY:x \
  -d '{
        "url": "https://webhook.site/6e76b64d-584c-4910-8ec6-xxxx",
        "events_types": ["invoice.created", "invoice.updated" ]
      }'

The above command returns JSON structured like this:

{
  "id": 2156,
  "url": "https://webhook.site/6e76b64d-584c-4910-8ec6-xxxx",
  "auth_key": "xxxx",
  "events_types": [
    "invoice.created",
    "invoice.updated"
  ],
  "last_sent_at": null,
  "last_error": null,
  "events_sent": 0,
  "created_at": "2021-09-30T07:57:34.000Z",
  "updated_at": "2021-09-30T07:57:34.000Z"
}

HTTP Request

POST /webhooks

Parameters

Parameter Type Description
url string The destination URL of the webhook request. Required
events_types array An array of strings indicating which events you wish to subscribe to. Check the available event types here. Required

Webhook URLs should be set up to accept HEAD and POST requests. When you provide the URL where you want Quaderno to POST the data for events, we'll do a quick check that the URL exists by using a HEAD request (not POST).

If the URL doesn't exist or returns something other than a 200 HTTP response to the HEAD request, Quaderno won't be able to verify that the URL exists and is valid.

This will return 201 Created with the current JSON representation of the webhook if the creation was a success.

Retrieve a webhook

GET /webhooks/WEBHOOK_ID.json

curl https://ACCOUNT_NAME.quadernoapp.com/api/webhooks/WEBHOOK_ID \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

{
    "id": 3,
    "url": "http://anotherapp.com/notifications",
    "auth_key": "xxxx",
    "events:_types": ["invoice.created","contact.deleted"],
    "last_sent_at": "2021-10-06T11:43:44.000Z",
    "last_error": null,
    "events_sent": 10,
    "created_at": "2013-07-13T14:12:01Z",
    "updated_at": "2013-07-17T14:09:59Z"
}

HTTP Request

GET /webhooks/WEBHOOK_ID

Parameters

Parameter Type Description
WEBHOOK_ID integer The ID of the webhook to retrieve. Required

Update a webhook

curl https://ACCOUNT_NAME.quadernoapp.com/api/webhooks/WEBHOOK_ID \
  -u YOUR_API_KEY:x \
  -X PUT \
  -d '{
        "url": "https://webhook.site/6e76b64d-584c-4910-8ec6-xxxx",
        "events_types": ["contact.updated", "contact.created" ]
      }'

The above command returns JSON structured like this:

{
    "id": 3,
    "url": "http://anotherapp.com/notifications",
    "auth_key": "xxxx",
    "events:_types": ["contact.updated", "contact.created"],
    "last_sent_at": null,
    "last_error": null,
    "events_sent": null,
    "created_at": "2013-07-13T14:12:01Z",
    "updated_at": "2013-07-17T14:09:59Z"
}

HTTP Request

PUT /webhooks/WEBHOOK_ID

Parameters

Parameter Type Description
WEBHOOK_ID integer The ID of the webhook to retrieve. Required
url string The destination URL of the webhook request. Required
events_types array An array of strings indicating which events you wish to subscribe to. Check the available event types here. Required

Delete a webhook

curl https://ACCOUNT_NAME.quadernoapp.com/api/webhooks/WEBHOOK_ID \
  -u YOUR_API_KEY:x \
  -X DELETE

HTTP Request

DELETE /webhooks/WEBHOOK_ID

Parameters

Parameter Type Description
WEBHOOK_ID integer The ID of the webhook to delete. Required

List all webhooks

curl https://ACCOUNT_NAME.quadernoapp.com/api/webhooks \
  -u YOUR_API_KEY:x

The above command returns JSON structured like this:

[
  {
    "id": 2,
    "url": "https://myawesomeapp.com/notifications",
    "auth_key": "xxxx",
    "events": ["contact.created", "contact.updated"],
    "last_sent_at": "2013-05-18T11:11:11Z",
    "last_error": null,
    "events_sent": null,
    "created_at": "2013-05-17T14:08:05Z",
    "updated_at": "2013-05-17T14:08:05Z"
  }
  {
    "id": 3,
    "url": "http://anotherapp.com/notifications",
    "auth_key": "xxxx",
    "events:_types": ["invoice.created", "contact.deleted"],
    "last_sent_at": "2021-10-06T11:43:44.000Z",
    "last_error":  null,
    "events_sent":  10,
    "created_at": "2013-07-13T14:12:01Z",
    "updated_at": "2013-07-17T14:09:59Z"
  }
]

HTTP Request

GET /webhooks

 Webhook payload examples

{
  "event_type": "invoice.created",
  "account_id": 21417,
  "data": {
    "object": {
      "evidence": null,
      "id": 2012730,
      "contact_id": 1247386,
      "tag_list": [],
      "number": "00023",
      "issue_date": "2021-10-06",
      "contact_name": "John",
      "currency": "EUR",
      "gross_amount_cents": 9910,
      "total_cents": 11800,
      "amount_paid_cents": 0,
      "po_number": null,
      "payment_details": null,
      "notes": null,
      "state": "outstanding",
      "subject": null,
      "street_line_1": null,
      "street_line_2": null,
      "city": null,
      "postal_code": null,
      "region": null,
      "country": "ES",
      "tax_id": null,
      "processor_id": null,
      "processor": null,
      "processor_fee_cents": null,
      "custom_metadata": {},
      "due_date": null,
      "permalink": "a760b05e7fa068792b8c0f6dcc04b2b8ce856960",
      "contact": {
        "first_name": "John",
        "last_name": null,
        "email": null,
        "contact_person": null
      },
      "gross_amount": "99.10",
      "total": "118.00",
      "amount_paid": "0.00",
      "exchange_rate": "1.000000",
      "items": [
        {
          "id": 5894250,
          "product_code": "prod_ebook1",
          "description": "How to live with taxes",
          "quantity": "1.0",
          "unit_price": "9.1",
          "discount_rate": "0.0",
          "tax_1_amount_cents": 0,
          "tax_1_name": "",
          "tax_1_rate": 0,
          "tax_1_country": "",
          "tax_2_amount_cents": 0,
          "tax_2_name": "",
          "tax_2_rate": 0,
          "tax_2_country": "",
          "subtotal_cents": "910.0",
          "total_amount_cents": 910,
          "discount_cents": "0.0",
          "taxes_included": false,
          "reference": "prod_ebook1",
          "tax_1_amount": "0.00",
          "tax_2_amount": "0.00",
          "unit_price_cents": "910.00",
          "discount": "0.00",
          "subtotal": "9.10",
          "total_amount": "9.10"
        },
        {
          "id": 5894251,
          "product_code": "prod_zzz",
          "description": "A new product example",
          "quantity": "1.0",
          "unit_price": "100.0",
          "discount_rate": "10.0",
          "tax_1_amount_cents": 1890,
          "tax_1_name": "IVA",
          "tax_1_rate": 21,
          "tax_1_country": "ES",
          "tax_2_amount_cents": 0,
          "tax_2_name": "",
          "tax_2_rate": 0,
          "tax_2_country": "",
          "subtotal_cents": "10000.0",
          "total_amount_cents": 10890,
          "discount_cents": "1000.0",
          "taxes_included": false,
          "reference": "prod_zzz",
          "tax_1_amount": "18.90",
          "tax_2_amount": "0.00",
          "unit_price_cents": "10000.00",
          "discount": "10.00",
          "subtotal": "100.00",
          "total_amount": "108.90"
        }
      ],
      "payments": []
    }
  }
}

In this example, the user duplicated an invoice using the App, so the configured URL for receiving our Webhook responses received a POST with a header x-quaderno-signature: containing the auth_key.

You can see the whole body payload of its related invoice.created event on the right.


Another useful example would be registering for tax thresholds events, like threshold.warning and threshold.exceeded.

Here it is a body payload response example:


{
  "event_type": "threshold.warning",
  "account_id": 20988,
  "data": {
    "object": {
      "name": "90%",
      "country": "AU",
      "region": null,
      "postal_code": null,
      "date": "2021-10-08T08:43:48.000Z"
    }
  }
}