REST API v1

Webhooks

The Pocket webhooks allow you to react to various events in real-time:

  • Incoming purchases
  • Payouts
  • Identification requests
  • Refunds

You'll receive these events for all orders you've created through the Pocket REST API v1.

Registration

Soon you'll be able to register your webhook endpoints by yourself. Until then, please contact us.

Webhook request

For each event you'll receive an HTTP request to every of your registered webhook endpoints.

The basic structure of these requests is as follows:

POST /webhook
Content-Type: application/json
User-Agent: Pocket-Webhook/1.0
X-Signature-256: 89cc7396704998f25bd0019bdc2ec30de78ff0c89add39685b30df608ba2a8d6
X-Correlation-Id: 7320abc0-c88f-4d72-b72c-deae658782e2
X-Retry-Count: 0
{
"event": "event.action",
"payload": {
"some": "payload"
}
}

Signature validation

It is highly recommended that you validate the X-Signature-256 header attached to every webhook request. It's the only way to confirm that the request originated from Pocket and thus its payload can be trusted.

The signature is created by calculating the HMAC-SHA256 of the request body using a shared secret key. You're free to choose a shared secret key during registration of your webhook endpoint.

The X-Signature-256 value is always prefixed with sha256=, so please prepend this prefix to your calculated signature before comparing.

See pocketbitcoin/sig-tools for an example on how to calculate the signature.

Correlation header

We recommend you to log the X-Correlation-Id header while processing a webhook request. In case of an issue, this information allows us to precisely track the request up to its origin.

Error handling

Webhook delivieries are automatically retried on failure. Anything other than HTTP 2xx is considered a failed delivery and will lead to a retry in 5 minutes. That delay is doubled on every further attempt.

In the future you'll be able to list and resend single webhook events, for example after scheduled maintenance of your system. Until then, please contact us.

Event types

The request payload depends on the type of event being sent. These are the possible event types:

Event TypeDescription
exchange.executedThe exchange was executed. Includes all execution details.
exchange.interruptedThe exchange is interrupted. A reason and an action is attached.
exchange.refundedThe exchange was refunded automatically or after exchange.interrupted. A reason is attached.
exchange.settledThe exchange was settled. Includes all execution details and all payout information.

Event exchange.executed

When buying bitcoin on-chain, the payout object is always null because the payout will happen at 22:00 on the same day. When buying bitcoin through lightning, the payout object is null, unless a lightning_address was provided and we were able to resolve an invoice through it. Please note that in this event type the reason is always null.

{
"event": "exchange.executed",
"payload": {
"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
}
}
FieldDescription
exchange_idstring Exchange identifier you can use to get an exchange.
order_idstring Order identifier you can use to get an order.
fee_ratedecimal Fee rate deducted from cost.
pairstring Exchange pair in base/quote currency. Either btc/eur, btc/chf or btc/gbp.
typestring Exchange type buy or sell.
costdecimal Cost of the exchange in quote currency eur, chf or gbp.
executed_onstring Exact date and time of the execution in ISO 8601.
amountdecimal Amount of the exchange in base currency btc.
ratedecimal Exchange rate in quote currency eur, chf or gbp.
feedecimal Fee in quote currency eur, chf or gbp.
actionobject Action only available for lightning payout methods.
reasonnull Only available in exchange.interrupted and exchange.refunded events.
payoutobject The payout details.

Event exchange.interrupted

This event signals that an action is required to continue the exchange.

{
"event": "exchange.interrupted",
"payload": {
"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": {
"code": "initiate_payout",
"payout_options": {
// ...
}
},
"reason": {
"code": "payout_failure"
},
"payout": {
// ...
}
}
}
FieldDescription
exchange_idstring Exchange identifier you can use to get an exchange.
order_idstring Order identifier you can use to get an order.
fee_ratedecimal Fee rate deducted from cost.
pairstring Exchange pair in base/quote currency. Either btc/eur, btc/chf or btc/gbp.
typestring Exchange type buy or sell.
costdecimal Cost of the exchange in quote currency eur, chf or gbp.
executed_onnull Only available in exchange.executed and exchange.settled event.
amountdecimal Only available in exchange.executed and exchange.settled event.
ratedecimal Only available in exchange.executed and exchange.settled event.
feedecimal Only available in exchange.executed and exchange.settled event.
actionobject Action needed to uninterrupt this exchange.
reasonobject Reason for the interruption.
payoutobject Details of the failed Payout attempt.

Interruption reasons

There is only one possible code available:

  • payout_failure - Indicates a payout failure. A detailed failure_code is included in the Payout object.

Interruption actions

The action needed to uninterrupt the exchange is provided in the action object.

There is only one possible code available:

  • initiate_payout - Indicates that a payout_options needs to be selected.
{
// ...
"action": {
"code": "initiate_payout",
"payout_options": {
"invoice": {
"lnurl": "LNURL1DP68GURN8GHJ7UR0VD4K2ARPWPCZ6EMFWSKKCMN4WFKZ6CMGV9HXUETV94CX7CMTV46XZURS9EMX2UNRV4KZUCTSWQHKZURF9AKXUATJDSHHW6T5DPJ8YCTH9AJNJVPEXQCK2VFDX5UX2E3DXSEKGVFDVG6RJVFDXGEN2WPEXG6N2VE5XE3SS87AS5",
"fee": 0,
"expires_on": "2023-05-23T08:58:42.086Z"
},
"channel": {
"lnurl": "LNURL1DP68GURN8GHJ7UR0VD4K2ARPWPCZ6EMFWSKKCMN4WFKZ6CMGV9HXUETV94CX7CMTV46XZURS9EMX2UNRV4KZUCTSWQHKZURF9AKXUATJDSHKX6RPDEHX2MP0V5UNQWFSX9JNZTF48PJKVTF5XDJRZTTZXSUNZTFJXV6NSWFJX56NXDPKVVRFMWRA",
"fee": 10000,
"expires_on": "2023-05-23T08:58:42.086Z"
}
}
}
}

To request a lightning invoice payout the lnurl withdraw request protocol can be use. Alternatively the API client can send an invoice directly through the PATCH exchange endpoint. The provided invoice must contain the exchange amount minus the payout fee. (zero in this example)

To request a lightning channel payout the lnurl channel request protocol can be use. Alternatively the API client can initiate the channel payout though the PATCH exchange endpoint.

Once the offered fee has expired you have to request a new fee offer before requesting a payout. You can request a new offer by calling the GET exchange endpoint (only expired fee offers will be renewed).

Interruption payout

The payout details are provided in the payout object:

{
"event": "exchange.interrupted",
"payload": {
// ...
"payout": {
"type": "invoice",
"status": "failed",
"failure_code": "no_route",
// ...
}
}
}
FieldDescription
typestring Whether an invoice or a channel payout failed.
statusstring Status failed.
failure_codestring The failure reason depending on the payout type.

There are multiple invoice payout failure_codes possible:

  • no_route - no rout could be found.
  • invoice_expired - the invoice expired.
  • already_paid - the invoice was already paid.
  • rejected - the invoice was rejected by the recipient.
  • error - something went wrong, try again.

There are multiple channel payout failure_codes possible:

  • min_chan_size - the receiving lightning node does allow such small channels.
  • connect_error - connecting to the lightning node failed.
  • push_amt_rejected - the receiving lightning node does not allow channel payouts.
  • error - something went wrong, try again.

Event exchange.refunded

Please note that in this event type all execution details are empty, including action and payout, but the reason field is set.

{
"event": "exchange.refunded",
"payload": {
"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
}
}
FieldDescription
exchange_idstring Exchange identifier you can use to get an exchange.
order_idstring Order identifier you can use to get an order.
fee_ratedecimal Fee rate deducted from cost.
pairstring Exchange pair in base/quote currency. Either btc/eur, btc/chf or btc/gbp.
typestring Exchange type buy or sell.
costdecimal Cost of the exchange in quote currency eur, chf or gbp.
executed_onnull Only available in exchange.executed and exchange.settled event.
amountdecimal Only available in exchange.executed and exchange.settled event.
ratedecimal Only available in exchange.executed and exchange.settled event.
feedecimal Only available in exchange.executed and exchange.settled event.
actionnull Only available in exchange.interrupted event.
reasonobject Reason for the refund.
payoutnull Only available in exchange.settled event.

Refund reasons

The reason why an exchange is refunded is provided in the reason object:

{
"reason": {
"code": "threshold_exceeded"
}
}

These are the possible reason codes:

  • threshold_exceeded - A threshold was exceeded and the exchange could not be processed, e.g. due to lack of full identification.
  • order_inactive - The order was set to inactive and cannot be used unless activated again.
  • companies_unsupported - Payment arrived from a company bank account which is not yet supported.
  • country_unsupported - Payment arrived from a country which is not yet supported.
  • other_risk_detected - Some other risk was detected that prevents this exchange from being executed.
  • customer_requested - The customer requested a refund of their payment.
  • account_not_matching - Payment arrived from another person than the one associated with the order.
  • payout_expired - The payout was not possible within the specified time window.

Event exchange.settled

Please note that in this event type the action and reason fields are always null.

{
"event": "exchange.settled",
"payload": {
"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": {
// details depending on payout type
}
}
}
FieldDescription
exchange_idstring Exchange identifier you can use to get an exchange.
order_idstring Order identifier you can use to get an order.
fee_ratedecimal Fee rate deducted from cost.
pairstring Exchange pair in base/quote currency. Either btc/eur, btc/chf or btc/gbp.
typestring Exchange type buy or sell.
costdecimal Cost of the exchange in quote currency eur, chf or gbp.
executed_onstring Exact date and time of the execution in ISO 8601.
amountdecimal Amount of the exchange in base currency btc.
ratedecimal Exchange rate in quote currency eur, chf or gbp.
feedecimal Fee in quote currency eur, chf or gbp.
actionnull Only available in exchange.interrupted event.
reasonnull Only available in exchange.refunded event.
payoutobject Different for on-chain coin payout, lightning invoice payout and lightning channel payout.

In case of lightning invoice payouts this event is only sent once. For lightning payment channel payouts, the event is sent twice (channel funding and channel open). For bitcoin on-chain payouts this event will be set multiple times. First once the transaction is broadcast, then called again if the transaction was replaced and finally once the transaction is confirmed in the blockchain.

Settled coin payout

The payout details are provided in the payout object:

{
"event": "exchange.settled",
"payload": {
// ...
"payout": {
"type": "coin",
"txid": "c01c7a0fd270aa3876119098c0e2d51687a795efab99787e214d8a93ab9f8342",
"outpoint": 1,
"bitcoin_address": "bc1q5vvayqt3n4alhjaxy6ql4w2fs6r0y83rmvh3tg",
"derivation_path": null,
"block_height": null,
"confirmations": 0,
"fee": 0.00000190,
"amount": 0.0019681
}
}
}
FieldDescription
typestring Payout type coin.
txidstring Bitcoin transaction identifier.
outpointstring Index of the output inside of the Bitcoin transaction.
bitcoin_addressdecimal Bitcoin address paid out to.
derivation_pathstring Derivation path to the bitcoin_address paid out to. Only available if Order was created with extended_public_key.
block_heightinteger Index of the Bitcoin block in which the Bitcoin transaction was first confirmed in. null until first confirmation.
confirmationsinteger Number of confirmations the Bitcoin transaction has received. 0 when broadcast, 1 on first confirmation.
feedecimal Bitcoin network fee deducted from payout.
amountdecimal Effective payout amount with the fee already deducted.

Settled invoice payout

The payout details are provided in the payout object:

{
"event": "exchange.settled",
"payload": {
// ...
"payout": {
"type": "invoice",
"status": "completed",
"failure_code": null,
"invoice": "lntb532230n1pj9y0jjpp5rvfx3xm8qldv4jv27h87z38dzr96kv5g2xz3nfhqqxpwetedfw3sdq5g9kxy7fqd9h8vmmfvdjscqzpgxqyz5vqsp5n00s9xjghllwpchkfwaxtufg0gy6w7vjwch27n29vg3j574tg50s9qyyssq7hmfgn963zptfgpjhkjese0kj794aavqjgzu9lrpa6wue08gryh3vclxcd6kr7f7v7k7g6akydrm8pk2uj3en77pecn7lh0776gvnnqq39466d",
"preimage": "39b25d4a9dedc51239d3d67e0df41d265b87105c519ba2230c77362ab20e08c8",
"fee": 0,
"amount": 0.0053223
}
}
}
FieldDescription
typestring Payout type invoice.
statusstring Status completed.
failure_codeAlways null for completed invoice payouts.
invoicestring Lightning bolt11 invoice.
preimagestring Payment preimage that proves that the invoice has been paid.
feedecimal Lightning network routing fee deducted from payout.
amountdecimal Effective payout amount with the fee already deducted.

Settled channel payout

The payout details are provided in the payout object:

{
"event": "exchange.settled",
"payload": {
// ...
"payout": {
"type": "channel",
"status": "funding",
"failure_code": null,
"host": "34.65.45.157:9736",
"txid": "d9390988deaeb25d29d8814c0e4a4efc1ed676485c15223ad23339f0d2db17ad",
"outpoint": 1,
"channel_id": null,
"fee": 0,
"amount": 0.0053223
}
}
}
FieldDescription
typestring Payout type channel.
statusstring Status funding once broadcast or completed once the channel is open.
failure_codeAlways null for successful channel payouts.
hoststring The host address of the lightning node.
txidstring Bitcoin transaction identifier for the funding transaction of the payment channel.
outpointstring Index of the output inside of the Bitcoin transaction.
channel_idstring Payment channel id once the channel is open, otherwise null.
feedecimal Bitcoin network fee deducted from payout.
amountdecimal Effective payout amount with the fee already deducted.