Overview

Webhook Data

A webhook consists of a URL to be called and a set of events that will trigger calls.

Several endpoints are provided by the OPENFORMAT API to manage your webhooks. Documentation for these endpoints can be found at the API Reference section.

HTTPS Requirement

To ensure security, all webhook URLs must use HTTPS and the server certificate has to be valid; expired or self-signed certificates will result on failed calls.

Supported Events

Currently, only the following event is implemented:

  • transaction: This event is triggered whenever one of your transactions is broadcasted in the blockchain. The payload for this event is the transaction receipt as created by the ehters.js library.

Webhook Calls

The body of webhook calls will be a JSON with the following fields:

  • event: A string with the event that triggered the call.
  • idempotency_key: A string that contains an idempotency key for the webhook call. You should ignore this call if your application already processed a call with the same key.
  • payload: A JSON object which will hold the relevant data for the triggered event.

Example body of a webhook call for a transaction event:

{
  "event": "transaction",
  "idempotency_key": "61966479-dbe6-4327-a6b7-ad7354c64c57",
  "payload": {
    "_type": "TransactionReceipt",
    "blockHash": "0xa7e8812cd44c01afa0c0ffca1a4bacebd957ed62196570f75868d513601fddf5",
    "blockNumber": 2,
    "contractAddress": null,
    "cumulativeGasUsed": "73602",
    "from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
    "gasPrice": "3517925438",
    "blobGasUsed": null,
    "blobGasPrice": null,
    "gasUsed": "73602",
    "hash": "0xa84276e7fb8b3fb9387d3e8bc58a96949028788d1e362f294c81818275e17c9f",
    "index": 0,
    "logs": [],
    "logsBloom": "0x00...000000",
    "status": 1,
    "to": "0x9716FB655f2A72b1FC1B4DB02B8ad20b6747442A"
  }
}

Timeout

All webhook calls have a timeout of 5 seconds. Do not perform heavy duty tasks before responding the webhook call to avoid exceeding the timeout.

A good practice is to immediately respond the webhook call and delegate the processing to a separate process or thread.

Redirects

The maximum number of redirects allowed in webhook calls is 3. More than that and the call will be considered failed.

Status code

Beside redirects (3xx), webhook calls expect a 2xx status code response. Any other status code will be considered a failed call.

Retry mechanism

If a webhook call fails, the OPENFORMAT API will attempt a maximum of 5 retries. The duration between retry attempts will follow an exponential backoff strategy.

If all 5 attempts fail, the webhook will be deactivated and no further calls will be executed on it until it is activated again.

Specs

You can check the API Reference section to see a list of each endpoint associated with the webhooks mechanism.

Creation

You can create a webhook by calling endpoint POST /api/v1/webhook and passing:

  • url: the HTTPS url of your webhook.
  • events: the list of events that will trigger the webhook call. If this parameter is not sent all events will be used as triggers.
curl --request POST \
  --url https://api.openformat.tech/v1/webhook \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: <api-key>' \
  --data '{
  "url": "https://api.example.com/webhook",
  "events": [
    "transaction"
  ]
}'  

The created webhook will be disabled by default, in order to start receiving events you need to verify it first.

Verification and Activation

Activation

This same process needs to be followed for activating a webhook that has been deactivated because of call failures.

Triggering a verification challenge

In order to start a webhook verification you need to call endpoint POST /api/v1/webhook/:id/test where :id is the id of the webhook to verify, example /api/v1/webhook/12/test.

This action will trigger a webhook call that needs to be responded correctly. The call will contain the following fields:

  • event: With value test.
  • idempotency_key: With an idempotency key for this call.
  • payload: With value null

Headers of the request will contain a x-openformat-signature header with a valid signature.

Example verification request:

POST /webhook HTTP/1.1
accept: application/json, text/plain, */*
content-type: application/json
user-agent: Openformat-Webhook-Client
x-openformat-signature: dIqk7OzudIQqWhkRVsxrGi7nJjV0oDDGimDSLukdlVE=
Host: mywebhookapi.example.com
Content-Length: 88

{
    "event": "test",
    "idempotency_key": "c4eec277-8a0d-4203-a113-ac5f360e0caa",
    "payload": null
}

Challenge response

In order to pass the challenge your response should have:

  • A valid SSL certificate.
  • A valid status code: 2xx
  • Content type header: application/json.
  • Response body will be a JSON with a single field challenge. This field will contain the value of the x-openformat-signature in the webhook call request.

Example response for the previous verification request:

HTTP 200 OK
content-type: application/json

{
    "challenge": "dIqk7OzudIQqWhkRVsxrGi7nJjV0oDDGimDSLukdlVE="
}

Once the verification is completed your webhook will be active and we will start sending calls to it.

Secrets

Generation

Webhook secrets are generated by the system and are associated with a user account when the account is created. Each user account is assigned a unique webhook secret.

This secret is used to sign all the calls that we send to your webhooks so you can confirm they come from OPENFORMAT API.

Retrieving Current Secret

An endpoint GET /api/v1/webhook/secret exists to retrieve the current webhook secret associated with the user account. This endpoint returns the current secret in the response.

curl --request GET \
  --url https://api.openformat.tech/v1/webhook/secret \
  --header 'X-API-KEY: <api-key>'

Generating New Secret

An endpoint POST /api/v1/webhook/secret exists to generate a new secret. This request has no body, and the secret is generated by the system and returned in the response. The new secret replaces the previous one associated with the user account.

curl --request POST \
  --url https://api.openformat.tech/v1/webhook/secret \
  --header 'X-API-KEY: <api-key>'

Signature verification

While not mandatory, we strongly recommend you to confirm that calls received in you webhooks are coming from the OPENFORMAT API.

Method

To confirm that a webhook call originated from the OPENFORMAT API use the following method:

  • Retrieve the value of x-openformat-signature header. All webhook calls executed by the OPENFORMAT API contain this header.
  • Retrieve the raw body of the call. This is a JSON object, meaning that the body begins with a { character and ends with a } character, inclusive.
  • Compute the HMAC-SHA256 hash of the body using your webhook secret as signing key.
  • Encode the result in base64 format.
  • Confirm that the encoded value is the same than x-openformat-signature header.

Example

Assume the following is a request to a webhook call from the OPENFORMAT API.

POST /webhook HTTP/1.1
accept: application/json, text/plain, */*
content-type: application/json
user-agent: Openformat-Webhook-Client
x-openformat-signature: dIqk7OzudIQqWhkRVsxrGi7nJjV0oDDGimDSLukdlVE=
Host: mywebhookapi.example.com
Content-Length: 88

{"event":"test","idempotency_key":"c4eec277-8a0d-4203-a113-ac5f360e0caa","payload":null}

Also, assume the webhook secret is f2ec0291-cf11-41ec-b9b6-bfaa218c745b.

Then we have:

body = '{"event":"test","idempotency_key":"c4eec277-8a0d-4203-a113-ac5f360e0caa","payload":null}'
secret = 'f2ec0291-cf11-41ec-b9b6-bfaa218c745b'

and we need to compute BASE64( HMAC-256( body, secret ) )

In Python we could solve it this way:

import hmac
import hashlib
import base64

hmac_sha256 = hmac.new(secret.encode("UTF-8"), body.encode('UTF-8'), hashlib.sha256)
digest = hmac_sha256.digest()
signature = base64.b64encode(digest).decode('UTF-8')
print(signature)
# prints 'dIqk7OzudIQqWhkRVsxrGi7nJjV0oDDGimDSLukdlVE='

which matches the value of the x-openformat-signature header.

Logs

Using endpoint GET /api/v1/webhook/:id/logs you can get the logs for calls executed for a specific webhook. In this URL :id is the identification of the webhook, example 123.

curl --request GET \
  --url https://api.openformat.tech/v1/webhook/12/logs \
  --header 'X-API-KEY: <api-key>'

You can paginate the result using query variables page and page_size.