Basics

Introduction

Rutter is a unified RESTful API for B2B Financial Products—connect to any commerce, payments, accounting, or ads system in a single API. We offer a range of product options with different capabilities and price points, as well as the ability to fine-tune our API to your needs.

Want to jump straight to the code?

Skip the introduction and dive into the Quickstart

Explore Quickstart

To get started, click through the endpoints to learn more about the functionality across our APIs and check out our Postman Collection to test our API endpoints.

Organization

API reference pages are divided by the object with which the endpoints interact. For example, there is a single page for Bill Payments, but within the page there are endpoints for creating, updating, and listing bill payments.

Each API reference page has roughly three sections:

  1. Overview

    A brief description of the Rutter entity and the set of endpoints.

  2. Object

    A description of the object representing the associated data model.

  3. Endpoints

    A list of endpoints available in the API. This will include a description of the endpoint, the HTTP method, the request and response body, and platform support.

Production Base URL
1
https://production.rutterapi.com/versioned
Sandbox Base URL
1
https://sandbox.rutterapi.com

Versioning

Rutter supports API versioning - all APIs are prefixed with /versioned/ and a version must be supplied in the header by using the key X-Rutter-Version. The supported values are: 2024-04-30, 2023-03-14, and 2023-02-07.

Breaking API changes are changes that disrupt the working implementation of our customers. This can happen if we change our endpoints in a way that our customers do not expect them to change. We have a strict policy to never make a breaking change on an published version of our API.

Things we do not consider as breaking changes:

  • Adding new API endpoints (e.g. POST /accounting/bill_credit_applications).
  • Adding new optional request parameters to existing API endpoints.
  • Making a previously required request parameter optional.
  • Adding new property fields to existing API responses.
  • Changing the order of property fields in existing API responses.
  • Changing the length or format of opaque strings, such as object IDs, error messages, and other human-readable strings.

All other API changes maybe considered a breaking change. This includes (but is not limited to):

  • Removing an API endpoint.
  • Making a previously optional request parameter required.
  • Adding a required request parameter to existing API endpoints.
  • Removing or renaming property fields from existing API responses.
  • Changing the property field type of existing API responses.
  • Changing the property field of existing API responses that was non-nullable as nullable.
  • Changing values of an enum type. Note that there are a couple of exceptions to this rule: we may add new values to enum's for platforms we support and for currency codes.

Please see the API upgrades page for more information on differences between our various API versions.

Versioned URL
1
https://production.rutterapi.com/versioned/accounting/accounts

Authorization

Authorizing Requests

Rutter uses your client_id and client_secret keys to control access to our API via HTTP Basic Auth. You can find your keys in your dashboard for each of our API environments (sandbox, production).

When making an API request to the server, Rutter expects your client_id and client_secret to be Base64 encoded within the Authorization header. The header is formed by concatenating the word Basic, followed by a space ( ), and a base64 encoded string of the client_id, a colon (:), and the client_secret.

Authorization: Basic base64({client_id}:{client_secret})

Authorizing Access for a Given Connection

To identify a specific connection, Rutter uses the access_token query parameter.

This token is generated by Rutter and is unique to each connection. The access token is acquired by hitting the Exchange Tokens endpoint.

Authenticated Request
1
curl "https://production.rutterapi.com/versioned/accounting/accounts" \
2
-H "X-Rutter-Version: 2023-02-07" \
3
-H "Authorization: Basic <YOUR_ENCODED_CLIENT_ID_AND_CLIENT_SECRET>"
Authenticated Request with Access Token
1
curl "https://production.rutterapi.com/versioned/accounting/accounts?access_token=<ACCESS_TOKEN>" \
2
-H "X-Rutter-Version: 2023-02-07" \
3
-H "Authorization: Basic <YOUR_ENCODED_CLIENT_ID_AND_CLIENT_SECRET>"

Errors and Error Codes

Rutter uses standard HTTP error codes that follow common industry practices. However, we also utilize a few custom error codes for specific conditions unique to Rutter. These custom codes are designed to offer detailed insights into the nature of the issue, accompanied by actionable error messages wherever possible.

If you are experience any of these errors and are unsure what to do, please reach out to our support team.

Actionable Error Codes

StatusSituationNotes
400Rutter will return this error when a request is made to Rutter with an invalid input. The error message should provide detailed information about the issue and suggestions. Use the information in the error message to identify and address any issues with the input data.
401Rutter will return this error when a request is made to Rutter with missing or invalid credentials. Please check that you have the correct credentials.
404Rutter will return this error when an endpoint is not implemented or is not supported for a given platform.
409Rutter will return this error when a request is made that differs from another request made with the same idempotency-key.
410Rutter will return this error when the endpoint is recognized but the requested resource is not found. This usually happens when the :id of the requested resource is invalid.
429Rutter will return this error when too many requests are being made to the Rutter service. Please allow for some time in between your requests.
450 (custom)Rutter will return this error when the underlying platform returns 400 Bad Request to differentiate from the 400 error Rutter itself returns. The error message will contain details about the problem and recommendations for resolution.
451 (custom)Rutter will return this error when the underlying platform returns an 401 Unauthorized to differentiate from the 401 error Rutter itself returns.
452 (custom)Rutter will return this error when the underlying platform returns a 429 Too Many Requests error to differentiate from the 429 error Rutter itself returns. This typically only happens for POST, PATCH, or DELETE endpoints. In such instances, we recommend leveraging the idempotent endpoint, as this enables Rutter to automatically retry the request using exponential backoff.

Server Error Codes

StatusSituationNotes
500Rutter will return this error in response to unforeseen circumstances. When this Internal Server Error happens, our team will be alerted and prioritize efforts to comprehend and address these issues promptly.
550 (custom)Rutter will return this error when the underlying platform returns 500 Internal Server Error to differentiate from the 500 error Rutter returns itself. In these situations, we will encode the platform error message in our error response. We recommend trying the request again after a delay.

Error Response

If you receive an error, you will receive an object with the following properties:

PropertyTypeDescription
error_typeStringThe type of error that occurred.
error_codeStringA more specific code under the error type.
error_messageStringA string that describes the error that occurred.
error_metadataObjectAn object containing details related to the error. Fields in here are experimental and subject to change:
  • source - Whether the error originated from Rutter ("rutter") or the platform ("platform").
  • human_readable - An easy to read error message for platform-originated errors, e.g. "The account type xxxx is incompatible with this transaction."
  • platform - An object that contains data from the platform, e.g. headers or rate limiting information.

Rate Limiting

When using Rutter's read endpoints, you will not experience the normal rate-limits imposed by a specific E-commerce platform (e.g. Shopify).

Rutter's write endpoints pass requests directly to their respective E-Commerce and Accounting platforms and will be subject to each platform's unique rate-limits.

How does Rutter resolve rate limit issues?

In order to offer fast performance to merchants' data, Rutter uses a combination of caching and webhooks to store the latest data for a merchant. When you send requests to Rutter's API (Products, Orders, and Customers), most of the time the data will be cached and returned immediately. One exception is when a merchant first authorizes you to use their data: Rutter will begin downloading the data immediately, but it will not be ready until the INITIAL_UPDATE webhook fires.

How does Rutter cache data and keep it up-to-date?

Rutter does an "initial sync" of a merchant's data when they authenticate your app. This may take hours to days depending on the size of the store, and the most requested data will be cached first. Once this process completes, Rutter subscribes to relevant webhooks on the E-commerce platform to keep the data in sync. As a final precaution, Rutter also regularly makes requests to platforms to double-check that no updates were lost from the webhooks.

Webhooks

You will receive notifications via a webhook whenever there are new events associated with a Connection or change in data.

Webhook Security

Webhook signatures provide additional security to webhooks emitted by Rutter by verifying that the webhook wasn't sent from an unknown or malicious entity. To do this, Rutter signs each webhook payload with a secret. The resulting signature is included in the header of the request, which you can then use to verify that the webhook received is from Rutter, guaranteeing the validity of the webhook.

Every webhook sent by Rutter contains a header called X-Rutter-Signature whose value is the prefix sha256= followed by a base64 encoded HMAC-SHA256 hash of the webhook payload, where the secret used to generate the hash is your organization's client secret.

Connection Webhooks

All webhooks related to a connection contain the type CONNECTION

Webhook Retries

You can implement rate-limiting of Rutter webhooks to handle load on your server. If we receive a failure HTTP status code from your server when sending a webhook, we will attempt to retry the webhook up to 6 times following an exponential backoff method. The 6 attempts will take place over a period of ~15 minutes. If the webhook fails to be delivered after 6 attempts, we will stop retrying and mark the webhook as failed.

Special Considerations

When working with webhooks, please keep the following in mind:

Webhooks that have the same payload may be sent multiple times. This can be caused by a variety of reasons, such as when the underlying raw platform data changes but not the standardized Rutter data object. Please make sure your webhook handler accounts for this behavior.

Webhooks are not 100% reliable. Expect a small percentage of webhooks to get dropped under normal working conditions. Make sure to handle these missing webhooks gracefully, so that the application still syncs even if no webhooks are received. We recommend calling the API endpoints regularly to check for updates in addition to using webhooks for real-time updates.

Iniitial Update

This webhook fires after an initial data download for a Connection has been completed. You can now send requests to Rutter to fetch merchant data.

Attempting to fetch data from a Connection without receiving the INITIAL_UPDATE webhook will return a PRODUCT_NOT_READY error.

If your organization has data batching enabled, this webhook will fire after a batch of historical data for a Connection has been synchronized. You can continue to fetch data while this process occurs.

Initial Update
1
{
2
"type": "CONNECTION",
3
"code": "INITIAL_UPDATE",
4
"connection_id": "3adc1519-0d6e-4222-83de-25b457cf42be",
5
"access_token": "313c3e00-2bbc-4b2e-a9dc-13abed546b65"
6
}

Entity Webhooks

Whenever data changes inside of a platform, you will receive a webhook notifying you of the change. There are three types of changes - creates, updates, and deletes.

Every entity webhook contains a type corresponding to the entity, a code corresponding to the type of change, the entity that has been affected, and the access token related to that entity.

Entity Created

Fired after a new entity of a certain type has been created for a connection.

Example Order Created Webhook
1
{
2
"type": "ORDER",
3
"code": "ORDER_CREATED",
4
"access_token": "313c3e00-2bbc-4b2e-a9dc-13abed546b65",
5
"connection_id": "222c3e00-2bbc-4b2e-a9dc-13abed546b65",
6
"order": {
7
// ... New order object
8
}
9
}

Entity Updated

Fired after a new entity of a certain type has been updated for a connection.

Example Account Updated Webhook
1
{
2
"type": "ACCOUNT",
3
"code": "ACCOUNT_UPDATED",
4
"access_token": "313c3e00-2bbc-4b2e-a9dc-13abed546b65",
5
"connection_id": "222c3e00-2bbc-4b2e-a9dc-13abed546b65",
6
"account": {
7
// ... Updated Account object
8
}
9
}

Entity Deleted

Fired after a new entity of a certain type has been deleted for a connection. The final state of the entity before deletion is returned.

Example Bill Deleted Webhook
1
{
2
"type": "BILL",
3
"code": "BILL_DELETED",
4
"access_token": "313c3e00-2bbc-4b2e-a9dc-13abed546b65",
5
"connection_id": "222c3e00-2bbc-4b2e-a9dc-13abed546b65",
6
"bill": {
7
// ... Created Bill object
8
}
9
}

Asynchronous Operations

For some endpoints, Rutter support different response_mode parameters. This parameter allows you to specify whether to:

  1. Call the API in a synchronous fashion & wait for the response. This is the default behavior if no response_mode is specified.
  2. Call the API in an asynchronous fashion, receive a response immediately with a URL pointer to the request job, and wait for the result to be updated via the URL.

Using the async response mode is recommended when:

  • You want to make idempotent operations to avoid creating duplicate requests when sending the same payload. You can accomplish this by passing the distinct value through the Idempotency-Key request header.
  • You have specific latency requirements, and do not wish to wait multiple seconds for a response from the Rutter endpoint.
  • You want to make a large set of operations at the same time. This allows Rutter to intelligently avoid rate limit when making many requests to an external platform.

Response Mode: Prefer Sync

If you set response_mode to be prefer_sync, the request will try to return a synchronous response if the entire product is created within 30 seconds. If it takes longer, we will return an async_response object that contains a URL pointer to be used to query the status of the request as it continues to make progress on our server. This async_response object is the same format as if you set response_mode to be async.

Response Mode: Async

If you set response_mode to be async, the request will be run asynchronously and the API will immediately return an async_response. Query the response_url field on async_response for the status of the request.

If an async_response is returned, it has the following properties:

PropertyTypeDescription
idStringA unique UUID string representing the ID of the Async Job.
response_urlStringA link to the Fetch a Job endpoint. By making a GET request to this URL, you can retrieve the status and result of the job.
statusStringThe status of the job. One of the following
  • prequeued
  • pending
  • success
  • failure

We suggest querying the response_url field for the status of the request. The response_url corresponds to the Fetch a Job endpoint, and will return the successful result or errors encountered during the request.

Job Webhook

Both synchronous and asynchronous requests are backed internally by an asynchronous job that executes your request. When an Asynchronous Job is successful, we will also send a webhook to your configured webhook URLs.

Asynchronous Request
1
curl -X POST "https://production.rutterapi.com/versioned/accounting/invoices?access_token=<ACCESS_TOKEN>" \
2
-H "X-Rutter-Version: 2023-02-07" \
3
-H "Authorization: Basic <YOUR_ENCODED_CLIENT_ID_AND_CLIENT_SECRET>"
Asynchronous Request Body
1
{
2
"response_mode": "async", // or "prefer_sync"
3
"invoice": {
4
"account_id": "00000000-0000-0000-0000-000000000000",
5
"customer_id": "00000000-0000-0000-0000-000000000000",
6
"due_date": "2023-01-02T02:34:56.000Z",
7
...
8
}
9
}
Asynchronous Response
1
{
2
id: "00000000-0000-0000-0000-000000000000",
3
status: "prequeued",
4
response_url: "https://production.rutterapi.com/v2/jobs/b7ed4fa7-dcc9-4f53-a994-7504a592cefe
5
}
Fetch Asynchronous Jobs
1
curl "https://production.rutterapi.com/v2/jobs/<JOB_ID>" \
2
-H "X-Rutter-Version: 2023-02-07" \
3
-H "Authorization: Basic <YOUR_ENCODED_CLIENT_ID_AND_CLIENT_SECRET>"
Asynchronous Jobs Response
1
{
2
"type": "JOB",
3
"code": "JOB_COMPLETED",
4
"job": {
5
"id": "3446b185-117f-46bc-939b-aa53de5c35c1",
6
"status": "success",
7
"request": {
8
"url": "https://production.rutterapi.com/versioned/products",
9
"method": "POST",
10
"body": {
11
// Asynchronous Job Request Body
12
}
13
},
14
"response": {
15
"http_status": 200,
16
"body": {
17
// Asynchronous Job Response Body
18
}
19
}
20
}
21
}

Idempotency

Asynchronous requests support idempotency - you can accomplish this by passing a distinct value through the Idempotency-Key request header, which serves as the unique key.

Asynchronous Job with Idempotency
1
curl -X POST "https://production.rutterapi.com/versioned/accounting/invoices?access_token=<ACCESS_TOKEN>" \
2
-H "X-Rutter-Version: 2023-02-07" \
3
-H "Authorization: Basic <YOUR_ENCODED_CLIENT_ID_AND_CLIENT_SECRET>" \
4
-H "Idempotency-Key: <IDEMPOTENCY_KEY>"

Filtering Objects

On many list endpoints, you can use the filter parameter to only return objects that match a certain criteria. Some examples of filterable endpoints are List Accounts and List Customers. To do this, you will use Rutter's custom query language as described below.

For string fields, you may use the operator = to return all objects matching the given value for that field. The string value must be surrounded by double quotes (e.g., "...").

String filter example
1
filter=document_number = "1196"

For number fields, you may use any of the following operators: =, <, <=, >, >=.

Number filter example 1
1
filter=balance = 100
Number filter example 2
1
filter=balance >= 0 AND balance <= 100

For timestamp fields, you may use any of the following operators: =, <, <=, >, >=. The timestamp value must be specified as a ISO 8601 timestamp or in YYYY-MM-DD format and surrounded by double quotes (e.g., "...").

Timestamp filter example 1
1
filter=created_at = "2024-04-29T20:35:14.000Z"
Timestamp filter example 2
1
filter=created_at >= "2024-03-01T20:35:14.000Z" AND created_at <= "2024-04-29T20:35:14.000Z"

Multiple conditions can be grouped together within sets of parentheses (). The boolean operators AND and OR are supported.

Compound query filter example 1
1
filter=document_number = "1196" OR (created_at >= "2024-03-01" AND created_at <= "2024-04-29")
Compound query filter example 2
1
filter=(balance >= 0 AND balance <= 100) OR (created_at >= "2024-03-01" AND created_at <= "2024-04-29")

An example using cURL to get all accounts with a balance between $0 and $100 (both inclusive) might look something like this.

cURL example
1
curl "https://production.rutterapi.com/versioned/accounting/accounts?access_token=<ACCESS_TOKEN>&filter=balance>=0+AND+balance<=100" \
2
-H "X-Rutter-Version: 2023-02-07" \
3
-H "Authorization: Basic <YOUR_ENCODED_CLIENT_ID_AND_CLIENT_SECRET>" \

Open API Spec

Have questions?

Contact support for personalized guidance.

Contact support