NAV Navbar
json

Introduction

Welcome to the Escher developer documentation. Escher is a developer platform for cryptocurrency applications. It comprises three components: KYC, Liquidity, and Money Movement.

Sandbox Environment

For testing and development please access our sandbox environment at the URLs below. The sandbox environment requires different credentials than the production APIs. For sandbox credentials, email dev@iterative.capital. Orders sent to the sandbox are simulated, but not executed.

REST API

https://dev.api.escher.app

Production Environment

For access, please email dev@iterative.capital

REST API

https://api.escher.app

KYC-AML

All users must be verified through our KYC system before attempting a purchase.

The KYC Verification is part of our Trading API and uses the Trading API Base URL:

https://api.escher.app

Escher App

coming soon Escher App you will be able to use our Escher App to easily perform KYC verification for your users.

Escher Cashout Deeplink

Escher app currently allows users to cashout small balances (under $3.5k USD) to USD via the Lightning Network and Zelle. This can be actioned via our cashout deeplink. Want to have a branded integration in Escher App? Get in touch here

Request cashout via deeplink to Escher App locally.

Cashout Deeplink Example

https://hub.escher.app/cashout/<wallet name>?amount=<amount in sats>

This runs a call to Generate a BOLT-11 Lightning invoice from the Lightning Cashouts endpoint for a specified amount, defined by the wallet making the request. Escher will listen for payments to the payment hash generated by the invoice and automatically convert any satoshis sent to this hash into USD. A payment for that amount will then be sent to the user's registered Zelle address. Invoices and payment hashes are one-time use, so each cashout must request a new invoice.

Param Description
wallet (required) string. The wallet requesting the cashout (Breez, Zeus, etc.)
amount (required) float. The number of satoshis to specify in the invoice.

Request

{
  "wallet": "Breez",
  "amount": 10000,
}

Response

{ 
    "invoice": "lnbc1pvjluezpp5qqqsyqcesq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w"
}

Escher Purchase Deeplink

Escher app allows registered users to purchase small balances (under $3.5k USD) of cryptocurrency via linked bank accounts and Zelle. This can be actioned via our purchase deeplink. Want to have a branded integration in Escher App? Get in touch here

Purchase Deeplink Example

https://hub.escher.app/purchase/<wallet name>?amount=<amount to purchase>&address=<address where coin should be delivered>&coin=<coin to purchase>

Once the purchase of the cryptocurrency has completed the user will be returned to the originating app via a deeplink.

Param Description
wallet (required) string. The wallet requesting the purchase.
amount (required) float. The Number of coins to purchase (not sats).
address (required) string. The destination address for the purchased coins.
coin (optional) string. The coin to purchase (defaults to BTC)

Request

{
  "wallet": "Breez",
  "amount": 10000,
  "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
  "coin": "BTC",
}

Response

{
  "success": true,
  "order": {
    "id": "0f0665a9-4274-418e-ba93-3b8bd4d5b1bf",
    "created_at": "2020-01-29T19:39:03.513046+00:00",
    "product_id": "BTC-USD",
    "ord_type": "limit",
    "price": "8650.00",
    "size": "10.0",
    "side": "buy",
    "settle_to": "ADDRESS",
    "status": "done",
    "executed_value": "86500.00"
  }
}

Liquidity API

The REST API has endpoints for order management and trading.

REST API Production URL

https://api.escher.app

Requests

All requests and responses are application/json content type and use appropriate HTTP response status codes for success and failure.

Errors

{
  "message": "Invalid Symbol"
}

Unless otherwise stated, bad requests will recieve a response with HTTP 4xx status codes. The body will also contain a message parameter indicating the cause. Your language's HTTP library should be configured to provide message bodies for non-2xx requests so that you can read the message field from the body.

Common error codes

Status Code Description Possible Cause
400 Bad Request Invalid request format
401 Unauthorized Invalid access token or bad header
403 Forbidden You do not have access to the requested resource
404 Not Found Invalid URL
500 Internal Server Error Escher had a problem handling your request

Success

A successful response is indicated by HTTP status code 200 and may contain an optional body. If the response has a body it will be documented under each resource below.

Types

Timestamps

Unless otherwise specified, timestamps from the API are returned in ISO 8601 with microseconds. Make sure you can parse the following ISO 8601 format. Your language should have a library that can handle this without issues.

2019-06-04T16:53:40.357515+00:00

Numbers

Decimal numbers are returned as strings to preserve full precision across platforms. When making a request, it is recommended that you also convert your numbers to strings to avoid truncation and precision errors.

Integer numbers are unquoted.

Strings

All strings should be encoded in utf-8 unless otherwise specified.

IDs

Most identifiers are UUID unless otherwise specified. When making a request which requires a UUID, both forms (with and without dashes) are accepted.

11ffe56f-fce2-4fba-80ec-ab50a54f5eef or 11ffe56ffce24fba80ecab50a54f5eef

Authentication

Getting an Access Token

Registered users can retrieve an access token which is used to access the REST API. Access tokens are valid for one hour from the time they are retrieved. A new access token can be retrieved either by signing-in again or by using the RefreshToken in the sign-in response.

Param Value
email
password
refreshToken if the refreshToken is specified, the email and password are not required.

Request

curl -H 'Content-Type: application/json' -d '{"email": "email@email.com", "password": "ExamplePassword111!"}' https://api.escher.app/sign-in
{
  "email": "user@company.com",
  "password": "xxxxxxxx",
}
{
  "refreshToken": "ayJasdsadJKV1QiLCJlb........",
}

Response (without 2FA)

{
  "AuthenticationResult": {
    "AccessToken": "eyJraWQiOiJMS3FrYUl..........",
    "ExpiresIn": 3600,
    "TokenType": "Bearer",
    "RefreshToken": "ayJasdsadJKV1QiLCJlb........",
    "IdToken": "ayJasdsadJKV1QiLCJlb..........."
  },
  "user_info": {
    "Username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "email_verified": "true",
    "phone_number_verified": "true",
    "nationality": "US",
    "last_name": "Doe",
    "account_type": "personal",
    "first_name": "John",
    "phone_number": "+xxxxxxxxxxx",
    "website": "www.example.com",
    "email": "john@doe.com"
  }
}

Response (with 2FA)

{ 
  "ChallengeName": "SOFTWARE_TOKEN_MFA",
  "Session":
   "4GOoO3QvQLtkpSQLS_ArA2Siiw9...",
  "ChallengeParameters": { 
      "USER_ID_FOR_SRP": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 
    } 
}

HTTP Request

POST /sign-in

The i2-ACCESS-KEY header should not be set to access this endpoint. Only the Content-Type: application/json header must be set.

Attributes

The AccessToken attribute returned in the response is a JSON web token granting access to the REST and Websocket APIs (see creating a request). Access tokens are valid for one hour from the time they are retrieved.

MFA

2FA is optional and may be activated through the MFA setup. Response from this endpoint differs based on whether or not 2FA is activated. If active, the Session & USER_ID_FOR_SRP strings will need to be saved and submitted to /mfa-respond (see mfa-respond) to complete the sign-in flow to retreive an accessToken.

Creating a Request

All REST requests must contain the following headers:

Header Content
i2-ACCESS-KEY Cognito AccessToken acquired from the /sign-in endpoint
Content-Type application/json

All request bodies should be valid JSON.

MFA

MFA Setup

To enable MFA:

  1. Retrieve a valid access token (/sign-in)
  2. Associate software token with an authentication app such as Authy or Google authenticator (/mfa-associate-totp)
  3. Verify TOTP token (/mfa-verify-totp).
  4. Turn MFA on (see /mfa-on)

MFA Sign-In

To sign-in with an MFA enabled account:

  1. Retrieve Session and USER_ID_FOR_SRP from /sign-in
  2. POST Session, USER_ID_FOR_SRP, and code strings to /mfa-respond)

Associate Software Token

Associate a software token with an authenticator app such as Authy or Google Authenticator. The SecretCode must be inputted manually or displayed as a QR code.

Note: If MFA is currently on, it will be turned off and the existing SecretCode is forgotten, invalidating any codes generated by existing authenticator applications. Once this request is made, you must either verify the SecretCode or generate another, if the it is forgotten.

Response

{
  "SecretCode": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

HTTP Request

GET /mfa-associate-totp

Verify Software Token

Verify TOTP code. Once a SecretCode has been inputted into authenticator application of choice, submit a code generated to verify.

Param Description
code Code received from authentication app (Authy, Google Authenticator)

Request

{
  "code": "xxxxxx"
}

Response

{
  "success": true
}

HTTP Request

POST /mfa-verify-totp

Respond to Auth Challenge

Respond to Auth Challenge received from /sign-in

Param Description
code Code received from authentication application (Authy, Google Authenticator, etc.)
username USER_ID_FOR_SRP received from /sign-in
session Session string retreived from /sign-in (only when 2FA is enabled)

Request

{
  "code": "xxxxxx",
  "username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "session": "rsQfM1h4ktcRMoARhWA1X8S-IEexStnWO..."
}

Response

{
  "AuthenticationResult": {
    "AccessToken": "eyJraWQiOiJMS3FrYUl..........",
    "ExpiresIn": 3600,
    "TokenType": "Bearer",
    "RefreshToken": "ayJasdsadJKV1QiLCJlb........",
    "IdToken": "ayJasdsadJKV1QiLCJlb..........."
  },
  "user_info": {
    "Username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "email_verified": "true",
    "phone_number_verified": "true",
    "nationality": "US",
    "last_name": "Doe",
    "account_type": "personal",
    "first_name": "John",
    "phone_number": "+xxxxxxxxxxx",
    "website": "www.example.com",
    "email": "john@doe.com"
  }
}

HTTP Request

POST /mfa-respond

MFA Status

Get MFA status

Response (Active)

{
  "PreferredMfaSetting": "SOFTWARE_TOKEN_MFA"
}

Response (Deactivated)

{
  "PreferredMfaSetting": null
}

HTTP Request

GET /mfa-status

Turn On

Turn MFA On

Response

{
  "success": true
}

HTTP Request

GET /mfa-on

Turn Off

Turn MFA Off

Response

{
  "success": true
}

HTTP Request

GET /mfa-off

Orders

The API only supports Request for Quote (RFQ)-style orders. Quotes are valid for 10 seconds, and all accepted quotes are guaranteed to be filled at their quoted price and size before expiry.

Orders can only be placed for amounts less than or equal to your current unsettled trade limit. Trades that would cause you to exceed this limit are automatically rejected.

See Products for supported currency pairs.

Request for Quote (RFQ)

Get a firm quote with a set expiration time.

Request

{
  "product_id": "BTC-USD",
  "base_currency_size": "10.0",
  "side": "buy"
}

Response

{
  "quote_id": "6377c3da-d3e4-48d9-a503-b047e141606c",
  "product_id": "BTC-USD",
  "base_currency": "BTC",
  "quote_currency": "USD",
  "price": "8650.00",
  "base_currency_size": "10.0",
  "quote_currency_size": "86500.00",
  "side": "buy",
  "created_at": "2019-10-29T15:32:02.327857+00:00",
  "expiry": "2019-10-29T15:35:02.327857+00:00"
}

HTTP Request

POST /quotes

Param Description
product_id Product to trade (see GET /products)
base_currency_size Size to trade in base currency (i.e. BTC if product is BTC-USD).
side buy or sell

Trading a Quote

A quote can be traded in its entirety or with partial execution.

A successful trade will return a 200 status code.

A failed trade will return a 400, 401, 403 or 500 HTTP status code.

In addition, a success flag is provided to indicate the execution status of the trade.

HTTP Request

POST /quotes/accept

Param Description
quote_id Quote to trade.
quantity (optional) A quantity to specify for partial execution. Must be less than or equal to the quote size.
note (optional) A note to store with the order in the order history.

Request

{
  "quote_id": "6377c3da-d3e4-48d9-a503-b047e141606c",
}
{
  "quote_id": "6377c3da-d3e4-48d9-a503-b047e141606c",
  "quantity": 0.5,
  "note": "bot trade"
}

Response

{
  "success": true,
  "quote_id": "6c1b33f4-84fe-40a1-a017-a7991d46fe84",
  "order": {
    "id": "0f0665a9-4274-418e-ba93-3b8bd4d5b1bf",
    "created_at": "2020-01-29T19:39:03.513046+00:00",
    "product_id": "BTC-USD",
    "ord_type": "limit",
    "time_in_force": "FOK",
    "price": "8650.00",
    "size": "10.0",
    "side": "buy",
    "settle_from": "ADDRESS_Z",
    "settle_to": "ADDRESS",
    "status": "done",
    "executed_value": "86500.00"
  }
}
{ 
    "success": false,
    "message": "Trade exceeds unsettled balance risk limit" 
}

Order History

Get order history

Request

GET /orders?limit=15&offset=10&begin_timestamp=2020-07-01T00:00:00Z&end_timestamp=2020-07-03T21:03:33Z
GET /orders

Response

[
  {
    "id": "4f3d072d-de83-4c3b-8fea-44f2610cbe86",
    "party_id": "735f231c-a7d1-43f9-899b-23cccf3efd22",
    "created_at_client": "2020-04-14T20:25:30.893000+00:00",
    "created_at_server": "2020-04-14T20:25:31.176114+00:00",
    "client_specified_oid": "6046a65e-b4b8-4253-855a-6086fa073a71",
    "product_id": "BTC-USD",
    "price": "6933.84",
    "order_size": "0.00500000",
    "order_type": "limit",
    "client_side": "buy",
    "time_in_force": "FOK",
    "settle_from": null,
    "insert_timestamp": "2020-04-14T20:25:31.177054+00:00",
    "update_timestamp": "2020-04-14T20:25:41.845559+00:00",
    "proxy": "735f231c-a7d1-43f9-899b-23cccf3efd22",
    "fx_rate": "1",
    "note": null,
    "fill_qty": "0.005",
    "fill_price": "6933.84",
    "order_status": "filled" 
  }
]

HTTP Request

GET /orders

Param Description
limit Number of records to return. Default is 100.
offset offset records for pagination. Default is 0.
begin_timestamp Timestamp to start order history. Default is 2019-00-00.
end_timestamp Timestamp for end of order history. Default is current timestamp.

executed_value is the filled size multiplied by the fill price, which may be equal to or less than the order size.

Assign Order

If you have an account set up with clients or sub-accounts, you can assign an order to a different party. You can only assign orders that you placed, and you can only assign an order once.

POST /assign_order

Param Description
party_id The ID of the party to assign the order to
order_id The ID of the order you wish to assign

Request

{
  "party_id": "20e25407-13a0-474e-a874-6887f3fce048",
  "order_id": "3a33bd00-58df-4488-8768-2aed911345a0"
}

Response

{
    "success": true
}

Balances

Get your current unsettled trade balances.

{
  "balances": [
    {
      "currency": "BRL", 
      "balance": "-1964.42", 
      "net_notional_value": "-343.77350", 
      "notional_currency": "USD", 
      "reference_price": "0.175"
    }, 
    {
      "currency": "BTC", 
      "balance": "5.64370400", 
      "net_notional_value": "53375.2995517619636000", 
      "notional_currency": "USD", 
      "reference_price": "9457.49450215"
    }, 
    {
      "currency": "NGN", 
      "balance": "673483", 
      "net_notional_value": "1496.479226", 
      "notional_currency": "USD", 
      "reference_price": "0.002222"
    }, 
    {
      "currency": "USD", 
      "balance": "-30566.05", 
      "net_notional_value": "-30566.05", 
      "notional_currency": "USD", 
      "reference_price": "1"
    }
  ], 
  "limit": {
    "balance_limit": 250000, 
    "limit_currency": "USD"
  }
}

HTTP Request

GET /balances

Details

The balance_limit field indicates the total unsettled net notional value that the account is permitted to have before settling its trades. Trades that would cause the unsettled net notional balance to exceed this limit are rejected by the system. The limit is typically denoted in USD as indicated by the limit_currency field.

Negative balances indicate that Escher owes you money. When you a place trade, typically you will have a negative balance on one currency and a positive balance on another. When calculating unsettled trade limits, only positive balances are taken into account.

Products

Get a list of available pairs for trading.

[
  {
    "product": "BTC-USD",
    "base_currency": "BTC",
    "quote_currency": "USD",
    "base_min_size": "0.001",
    "base_max_size": "1000",
    "base_ref_price": "9401.71148052",
    "quote_ref_price": "1",
    "quote_increment": "0.01",
    "quotable": true,
    "active": true,
    "base_precision": 8,
    "quote_precision": 2
  },
  {
    "product": "BTC-AED",
    "base_currency": "BTC",
    "quote_currency": "AED",
    "base_min_size": "0.001",
    "base_max_size": "1000",
    "base_ref_price": "9401.71148052",
    "quote_ref_price": "0.27229408",
    "quote_increment": "0.01",
    "quotable": true,
    "active": true,
    "base_precision": 8,
    "quote_precision": 2
  }
]

HTTP Request

GET /products

Details

The base_min_size and base_max_size fields define the min and max order size. The base_max_size is unrelated to trade limits on an account and serves as a sanity check for abnormally large orders.

The quote_increment field specifies the min order price as well as the price increment.

The order price must be a multiple of this increment (i.e. if the increment is 0.01, order prices of 0.001 or 0.021 would be rejected).

The base_precision and quote_precision fields define the number of decimal points of divisibility available for the respective currency. For example, BTC is divisible to 8 decimal points (eg. 0.00000001 BTC) and USD is divisible to 2 (eg. $0.01).

Products currently supported include BTC-USD, BTC-AED, BTC-BRL, ETH-BRL, ETH-USD, ETH-BTC, USDT-BRL, USDT-USD

Parties

Response

[
  {
    "id": "20e25407-13a0-474e-a874-6887f3fce048",
    "party_type": "user",
    "party_name": "evan-test",
    "balance_limit": 30000,
    "limit_currency": "USD"
  },
  {
    "id": "a27d9d29-dc91-49f7-bd8a-4e6dff47fd60",
    "party_type": "user",
    "party_name": "chris-test",
    "balance_limit": 200000,
    "limit_currency": "USD"
  }
]

HTTP Request

GET /parties

Get a list of available parties. Any parties here can be assigned trades through the dashboard or Trader App.

Wallets

GET /wallets

Response

[
  {
    "party_id": "7cd134fe-c340-4504-97e5-51967f8a3fea",
    "address": "14y5p9Q8zC7st26FJm9W3wCy7F89yqdzTk",
    "currency": "BTC",
    "disabled": false,
    "insert_timestamp": "2020-05-19T18:47:01.320417+00:00",
    "update_timestamp": null,
    "note": null,
    "address_type": "fireblocks_deposit"
  },
  {
    "party_id": "7cd134fe-c340-4504-97e5-51967f8a3fea",
    "address": "3AWj1rZn5CqNmGeZvUZvhkndKLG87GgTkZ",
    "currency": "BTC",
    "disabled": false,
    "insert_timestamp": "2020-06-05T23:40:26.347422+00:00",
    "update_timestamp": null,
    "note": "Zeus",
    "address_type": "escher_wallet"
  }
]

Money Movement

Coming soon. Escher is building infrastructure to automate USD settlement. Until then, we settle with clients via Wire Transfer.

Contact

To request access, get in touch here.