REST API v1

Resources

ResourceDescription
ChallengeA one-time challenge required in the signed message when creating an order.
OrderA long-living order holding payment and payout instructions.
ExchangeAn exchange initiated by funding a previously created order.

Download our Postman Collection to find examples on how to work with these resources.

Exchange pairs

PairBase currencyQuote currency
btc/eurbtceur
btc/chfbtcchf
btc/gbpbtcgbp

Please note that cost, rate and fee are always denominated in the quote currency eur, chf or gbp, while the amount is denominated in the base currency btc.

Exchange types

TypeDescription
buyBuy base currency btc for quote currency eur, chf or gbp.
sellSell base currency btc for quote currency eur, chf or gbp.

Proof of ownership

Pocket needs to make sure that the addresses it pays out to are controlled by its users themselves. It does this by requesting a signature that can only be produced with the private key of the receiving address. The message that is signed can be chosen freely but must contain a previously generated Challenge token. The same signature requirement applies to lightning payout_methods.

Affiliation

All orders created through your api client automatically get affiliated to you. This means that part of the fees is attributed and paid out to you, according to the conditions defined during registration. If you have multiple agreements, you can use the optional affiliate_id field to choose which affiliate agreement to use.

Create a challenge

You'll need to create a challenge before you can create a new order. Its random token will have to be included in the message to be signed.

POST /v1/challenges
{}
201 created challenge
{
"id": "95ab94f0-bd8d-410c-bfc8-b1f27d43b0c0",
"token": "7v7t4Fmb",
"expires_on": "2022-01-10T00:25:11.123Z",
"completed_on": null
}

Get a challenge

Although unlikely, if you ever need to retrieve a previously created challenge using your client_id, you can do that very simply.

GET /v1/challenges/{id}
200 existing challenge
{
"id": "95ab94f0-bd8d-410c-bfc8-b1f27d43b0c0",
"token": "7v7t4Fmb",
"expires_on": "2022-01-10T00:25:11.123Z",
"completed_on": null
}

Create buy order

An order is a long-living set of a payment and payout instructions. You create it once and it can be used for one-time Ethereum purchases, but also for standing orders. The order also allows you to define the fee_rate applied to all exchanges (1.2% - 4.5%).

POST /v1/orders
{
"type": "buy",
"active": true,
"affiliate_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6", // optional
"fee_rate": 0.03,
"payment_method": {
"currency": "chf",
"debitor_iban": "DE75512108001245126199"
},
"payout_method": {
"ethereum_address": "17wvJajWQjoVJuJe31tuiDs4MmVeBfSvGY",
"message": "I confirm my ethereum wallet. [7v7t4Fmb]",
"signature": "IKiJsfsAta7Id7Seq+j8EEfpFUj2LCVDRjQ+ZC3/akv+VfUznwmq9mkxQtvqz8NTr0t6VnmTBcsEhHLMQYixHZM="
}
}
201 created order
{
"id": "64decab6-4129-4fe7-9f6e-1db68283f5ce",
"type": "buy",
"active": true,
"created_on": "2021-09-10T04:55:36.887Z",
"affiliate_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"fee_rate": 0.03,
"payment_method": {
"currency": "chf",
"debitor_iban": "DE75512108001245126199",
"creditor_reference": "RF18GW8K79",
"creditor_iban": "CH9400778214768302002",
"creditor_bank_name": "Luzerner Kantonalbank",
"creditor_bank_street": "Pilatusstrasse 12",
"creditor_bank_postal_code": "6002",
"creditor_bank_town": "Luzern",
"creditor_bank_country": "CH",
"creditor_bank_bic": "LUKBCH2260A",
"creditor_name": "Pocket App",
"creditor_street": "Industriestrasse 33",
"creditor_postal_code": "5242",
"creditor_town": "Lupfig",
"creditor_country": "CH",
"swiss_qr_bill_payload": null
},
"payout_method": {
"ethereum_address": "17wvJajWQjoVJuJe31tuiDs4MmVeBfSvGY",
"message": "I confirm my ethereum wallet. [7v7t4Fmb]",
"signature": "IKiJsfsAta7Id7Seq+j8EEfpFUj2LCVDRjQ+ZC3/akv+VfUznwmq9mkxQtvqz8NTr0t6VnmTBcsEhHLMQYixHZM="
}
}

Extended public key

To prevent address reuse when re-using an order, the payout method can be extended with the extended_public_key. The derivation_path is also required to identify the location of the ethereum_address. This allows Pocket to derive a fresh unused address for every payout.

{
// ...
"payout_method": {
"ethereum_address": "17wvJajWQjoVJuJe31tuiDs4MmVeBfSvGY",
"message": "I confirm my ethereum wallet. [7v7t4Fmb]",
"signature": "IKiJsfsAta7Id7Seq+j8EEfpFUj2LCVDRjQ+ZC3/akv+VfUznwmq9mkxQtvqz8NTr0t6VnmTBcsEhHLMQYixHZM=",
"extended_public_key": "xpub6C9cxxj5hCqTY7sYeofQPA4nXuLJpL5xBL5F874n8hremDiwrWPsPBmxjJxHqBL7jwxKy33j4uJmHXQSNxRegEY2MukehJUe8s4mBkaFGBU",
"derivation_path": "0/1",
"gap_limit": null // default 10
}
}
201 created order
{
// ...
"payout_method": {
"ethereum_address": "17wvJajWQjoVJuJe31tuiDs4MmVeBfSvGY",
"message": "I confirm my ethereum wallet. [7v7t4Fmb]",
"signature": "IKiJsfsAta7Id7Seq+j8EEfpFUj2LCVDRjQ+ZC3/akv+VfUznwmq9mkxQtvqz8NTr0t6VnmTBcsEhHLMQYixHZM=",
"extended_public_key": "xpub6C9cxxj5hCqTY7sYeofQPA4nXuLJpL5xBL5F874n8hremDiwrWPsPBmxjJxHqBL7jwxKy33j4uJmHXQSNxRegEY2MukehJUe8s4mBkaFGBU",
"derivation_path": "0/1",
"gap_limit": null
}
}

Lightning payout method

Apart from an on-chain payout method, you can also configure your order's exchanges to be paid out through Lightning, by specifying your node_pubkey. If a lightning_address is provided, an automatic payout attempt is initiated upon the exchange of each incoming payment, provided an invoice can be retrieved from the respective Lightning node.

{
// ...
"payout_method": {
"node_pubkey": "02765a281bd188e80a89e6ea5092dcb8ebaaa5c5da341e64327e3fadbadcbc686c",
"message": "I confirm my ethereum wallet. [7v7t4Fmb]",
"signature": "rywfek6717yuqpfpjmghyf173obgswr9uw5wbfsfhc8exjomftm71st4cyxprzkx5juxiokhbxm8rzkxoz8e3zmmpa644tudgt119s91",
"lightning_address": "vitalikn@getalby.com" // optional
}
}
201 created order
{
// ...
"payout_method": {
"node_pubkey": "02765a281bd188e80a89e6ea5092dcb8ebaaa5c5da341e64327e3fadbadcbc686c",
"message": "I confirm my ethereum wallet. [7v7t4Fmb]",
"signature": "rywfek6717yuqpfpjmghyf173obgswr9uw5wbfsfhc8exjomftm71st4cyxprzkx5juxiokhbxm8rzkxoz8e3zmmpa644tudgt119s91",
"lightning_address": "vitalikn@getalby.com"
}
}

Create sell order new

In contrast to buy orders, sell orders are intended for one-time use only. Before using this new feature, please contact us to clarify the exact terms.

POST /v1/orders
{
"type": "sell",
"active": true,
"affiliate_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6", // optional
"fee_rate": 0.015,
"payment_method": {
"type": "ethereum" // or lightning
},
"payout_method": {
"currency": "chf",
"iban": "DE75512108001245126199",
"beneficiary": {
"type": "individual",
"first_names": "Vitalik",
"last_names": "Buterin",
"address": {
"street": "Mempool 21",
"town": "Lugano",
"postal_code": "6900",
"country": "CH"
}
}
}
}
201 created order
{
"id": "73db19c8-719c-402e-b237-aca9e8088614",
"type": "sell",
"active": true,
"created_on": "2024-07-18T09:10:06.360Z",
"affiliate_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"fee_rate": 0.015,
"payment_method": {
"ethereum_address": "tb1qyc2a6vwc2unjke84vdsr0cld8znqtu3ghyde0r"
},
"payout_method": {
"currency": "chf",
"iban": "DE75512108001245126199",
"beneficiary": {
"type": "individual",
"first_names": "Vitalik",
"last_names": "Buterin",
"address": {
"street": "Mempool 21",
"additional": null,
"postal_code": "6900",
"town": "Lugano",
"country": "CH"
}
}
}
}

Patch an order

You can patch the active and fee_rate fields of an existing order created with your client_id at any time.

PATCH /v1/orders/{id}
{
"active": false, // optional
"affiliate_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6", // optional
"fee_rate": 0.025 // optional
}
200 patched order
{
"id": "64decab6-4129-4fe7-9f6e-1db68283f5ce",
"active": false,
"created_on": "2021-09-10T04:55:36.887Z",
"affiliate_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"fee_rate": 0.025,
"payment_method": {
"currency": "chf",
"debitor_iban": "DE75512108001245126199",
"creditor_reference": "RF18GW8K79",
"creditor_iban": "CH9400778214768302002",
"creditor_bank_name": "Luzerner Kantonalbank",
"creditor_bank_street": "Pilatusstrasse 12",
"creditor_bank_postal_code": "6002",
"creditor_bank_town": "Luzern",
"creditor_bank_country": "CH",
"creditor_bank_bic": "LUKBCH2260A",
"creditor_name": "Pocket App",
"creditor_street": "Industriestrasse 33",
"creditor_postal_code": "5242",
"creditor_town": "Lupfig",
"creditor_country": "CH",
"swiss_qr_bill_payload": null
},
"payout_method": {
"ethereum_address": "17wvJajWQjoVJuJe31tuiDs4MmVeBfSvGY",
"message": "I confirm my ethereum wallet. [7v7t4Fmb]",
"signature": "IKiJsfsAta7Id7Seq+j8EEfpFUj2LCVDRjQ+ZC3/akv+VfUznwmq9mkxQtvqz8NTr0t6VnmTBcsEhHLMQYixHZM=",
"extended_public_key": "xpub6C9cxxj5hCqTY7sYeofQPA4nXuLJpL5xBL5F874n8hremDiwrWPsPBmxjJxHqBL7jwxKy33j4uJmHXQSNxRegEY2MukehJUe8s4mBkaFGBU",
"derivation_path": "0/1",
"gap_limit": null
}
}

Get an order

You can retrieve all orders created using your client_id at any time.

GET /v1/orders/{id}
200 existing order
{
"id": "64decab6-4129-4fe7-9f6e-1db68283f5ce",
"type": "buy",
"active": true,
"created_on": "2021-09-10T04:55:36.887Z",
"affiliate_id": "8ed13c2a-a8c6-4f0e-b43e-3fdbf1f094a6",
"fee_rate": 0.03,
"payment_method": {
"currency": "chf",
"debitor_iban": "DE75512108001245126199",
"creditor_reference": "RF18GW8K79",
"creditor_iban": "CH9400778214768302002",
"creditor_bank_name": "Luzerner Kantonalbank",
"creditor_bank_street": "Pilatusstrasse 12",
"creditor_bank_postal_code": "6002",
"creditor_bank_town": "Luzern",
"creditor_bank_country": "CH",
"creditor_bank_bic": "LUKBCH2260A",
"creditor_name": "Pocket App",
"creditor_street": "Industriestrasse 33",
"creditor_postal_code": "5242",
"creditor_town": "Lupfig",
"creditor_country": "CH",
"swiss_qr_bill_payload": null
},
"payout_method": {
"ethereum_address": "17wvJajWQjoVJuJe31tuiDs4MmVeBfSvGY",
"message": "I confirm my ethereum wallet. [7v7t4Fmb]",
"signature": "IKiJsfsAta7Id7Seq+j8EEfpFUj2LCVDRjQ+ZC3/akv+VfUznwmq9mkxQtvqz8NTr0t6VnmTBcsEhHLMQYixHZM=",
"extended_public_key": "xpub6C9cxxj5hCqTY7sYeofQPA4nXuLJpL5xBL5F874n8hremDiwrWPsPBmxjJxHqBL7jwxKy33j4uJmHXQSNxRegEY2MukehJUe8s4mBkaFGBU",
"derivation_path": "0/1",
"gap_limit": null
}
}

Get an exchange

You can retrieve all exchanges initiated by orders created using your client_id at any time.

GET /v1/exchanges/{id}
200 executed exchange
{
"id": "a2d75998-5ee5-4501-a4cf-4e3788732b7a",
"order_id": "64decab6-4129-4fe7-9f6e-1db68283f5ce",
"fee_rate": 0.015,
"pair": "btc/chf",
"type": "buy",
"cost": 100.00,
"executed_on": "2021-09-15T14:35:10.122Z",
"amount": 0.00197000,
"rate": 50000.00,
"fee": 1.50,
"action": null,
"reason": null,
"payout": null
}
200 interrupted exchange
{
"id": "a2d75998-5ee5-4501-a4cf-4e3788732b7a",
"order_id": "64decab6-4129-4fe7-9f6e-1db68283f5ce",
"fee_rate": 0.015,
"pair": "btc/chf",
"type": "buy",
"cost": 100.00,
"executed_on": null,
"amount": null,
"rate": null,
"fee": null,
"action": {
"code": "identification_required",
"identification_id": "7bf967b5-3d34-4cd6-936c-6631811401d2"
},
"reason": null,
"payout": null
}
200 refunded exchange
{
"id": "a2d75998-5ee5-4501-a4cf-4e3788732b7a",
"order_id": "64decab6-4129-4fe7-9f6e-1db68283f5ce",
"fee_rate": 0.015,
"pair": "btc/chf",
"type": "buy",
"cost": 100.00,
"executed_on": null,
"amount": null,
"rate": null,
"fee": null,
"action": null,
"reason": {
"code": "threshold_exceeded"
},
"payout": null
}
200 settled exchange
{
"id": "a2d75998-5ee5-4501-a4cf-4e3788732b7a",
"order_id": "64decab6-4129-4fe7-9f6e-1db68283f5ce",
"fee_rate": 0.015,
"pair": "btc/chf",
"type": "buy",
"cost": 100.00,
"executed_on": "2021-09-15T14:35:10.122Z",
"amount": 0.00197000,
"rate": 50000.00,
"fee": 1.50,
"action": null,
"reason": null,
"payout": {
"txid": "c01c7a0fd270aa3876119098c0e2d51687a795efab99787e214d8a93ab9f8342",
"outpoint": 1,
"ethereum_address": "bc1q5vvayqt3n4alhjaxy6ql4w2fs6r0y83rmvh3tg",
"derivation_path": null,
"block_height": null,
"confirmed": false,
"fee": 0.00000190,
"amount": 0.0019681
}
}

Patch an exchange

Exchanges created for lightning orders require additional instructions on how the exchanged ethereum should be paid out. The different payout options and fee conditions are included in the webhook exchange.executed event payload.

Invoice payout

In case the receiving lightning node has enough inbound liquidity, this payout option is preferable.

PATCH /v1/exchanges/{id}
{
"payout": {
"type": "invoice",
"invoice": "lnbcrt585180n1pjrucjgpp5n64x8ugk4tuz3wkjuuqkmvsgf9xetq8l4ntj7nsr373zpmd5yvdsdqqcqzpgxqyz5vqsp5dhxsdywke3fuz3fhcv79n7y00zdxqdhz8japmpx2hgk5pa92llxs9qyyssqtk0wr35e65yupmsnzp9n3r2ynqdv420kt9rnukwxwh744mmsusnn2600wpdfpcsx5r2cjsc0t5583fhrgcsejlqp7uept6tv0tnufdqp4jwez5"
}
}
200 patched exchange
{
"id": "a2d75998-5ee5-4501-a4cf-4e3788732b7a",
"order_id": "64decab6-4129-4fe7-9f6e-1db68283f5ce",
"fee_rate": 0.015,
"pair": "btc/chf",
"type": "buy",
"cost": 100.00,
"executed_on": "2021-09-15T14:35:10.122Z",
"amount": 0.00197000,
"rate": 50000.00,
"fee": 1.50,
"action": null,
"reason": null,
"payout": {
"type": "invoice",
"status": "pending",
"invoice": "lnbcrt585180n1pjrucjgpp5n64x8ugk4tuz3wkjuuqkmvsgf9xetq8l4ntj7nsr373zpmd5yvdsdqqcqzpgxqyz5vqsp5dhxsdywke3fuz3fhcv79n7y00zdxqdhz8japmpx2hgk5pa92llxs9qyyssqtk0wr35e65yupmsnzp9n3r2ynqdv420kt9rnukwxwh744mmsusnn2600wpdfpcsx5r2cjsc0t5583fhrgcsejlqp7uept6tv0tnufdqp4jwez5",
"preimage": null,
"fee": 0,
"amount": 0.00197000
}
}
400 bad request
{
"error": "unexpected_node_pubkey",
"documentation_uri": "https://pocketethereum.com/developers"
}

Other bad request error codes are:

  • in_progress - the payout is already in progress.
  • already_completed - the payout is already completed.
  • invoice_amount - Invoice contains no or wrong amount.
  • invoice_network - Invoice for wrong ethereum network.
  • invoice_expired - The invoice is expired.
  • unexpected_node_pubkey - Invoice from the wrong lightning node.
  • invalid_invoice - Invoice not valid.

Channel payout beta

In case the receiving node has not enough inbound liquidity, this payout method can be used to open a new payment channel. The exchanged amount of ethereum is paid out by leveraging the push_msat field during channel negotiation (see BOLT 2).

PATCH /v1/exchanges/{id}
{
"payout": {
"type": "channel",
"node_address": "02765a281bd188e80a89e6ea5092dcb8ebaaa5c5da341e64327e3fadbadcbc686c@34.65.45.157:9736" // optional
"private": true // optional
}
}
200 patched exchange
{
"id": "a2d75998-5ee5-4501-a4cf-4e3788732b7a",
"order_id": "64decab6-4129-4fe7-9f6e-1db68283f5ce",
"fee_rate": 0.015,
"pair": "btc/chf",
"type": "buy",
"cost": 100.00,
"executed_on": "2021-09-15T14:35:10.122Z",
"amount": 0.00197000,
"rate": 50000.00,
"fee": 1.50,
"action": null,
"reason": null,
"payout": {
"type": "channel",
"status": "pending",
"node_address": "02765a281bd188e80a89e6ea5092dcb8ebaaa5c5da341e64327e3fadbadcbc686c@34.65.45.157:9736",
"private": true,
"txid": null,
"outpoint": null,
"channel_id": null,
"fee": 0.0001,
"amount": 0.00187000,
}
}
400 bad request
{
"error": "connect",
"documentation_uri": "https://pocketethereum.com/developers"
}

Other bad request error codes are:

  • in_progress - the payout is already in progress.
  • already_completed - the payout is already completed.
  • channel_payout_not_allowed - Channel payout is not allowed, a direct channel exists.
  • node_address_required - Unable to resolve the lightning node address. Please provide the node_address and try again.
  • unexpected_node_pubkey - Unexpected node pubkey in provided node_address.
  • connect - Unable to connect to the lightning node.