Add a Webhook

This tutorial explains how to add a webhook callback to get notified of incoming (received) document transactions.

If you prefer importing this tutorial into Postman or Insomnia, download the har archive file and import into your preferred tool.

Prerequisites

Your user must have the orgadmin role in an existing organisation to be able to proceed with this tutorial. Visit the Create an Organisation tutorial if you do not already belong to an organisation.

You must also be able to send a transaction to yourself in order to trigger the event in this tutorial. If you do not know how to do that, visit the Send a tutorial.

Step 1. List Existing Webhooks

If you are a new customer and it is the first time you run this tutorial your organisation will most likely not have any existing webhooks. But just to make sure, and at the same time test the first webhooks endpoint let us call /orgs/:orgId/webhooks to see what we get.

curl -X GET https://api.arratech.com/orgs/YOUR_ORGS_ID/webhooks \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

All is good if this calls returns a list of webhook objects, most likely an empty list in our case.

Step 2. Create a Callback Handler

Our future notification for an incoming transaction must be handled somewhere. Typically this is handled by a customer created service but to not make you build such a service as part of this tutorial we will use the free online tool webhook.site for it.

In a new browser window, navigate to https://webhook.site/. This will automatically generate a unique webhook endpoint url for you on the form
https://webhook.site/421d513b-ab88-4006-9c99-000000000000.

On this page you can now in real-time monitor all incoming calls to the generated endpoint url above. Take note of this url because we will use it in the next step.

Step 3. Register the Webhook

We are now ready to register the webhook. For this we need the callback url from the previous step and we also need to know the name of the "received a transaction" event which we are interested in. This we find in the API Documentation and it's transaction.received. We will also add a test event webhook.test which we will use to manually test our webhook once created.

We can now register a new webhook via the /orgs/:orgId/webhooks endpoint.

curl -X POST https://api.arratech.com/orgs/YOUR_ORGS_ID/webhooks \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "description": "My first hook",
	"events": ["transaction.received", "webhooks.test"],
	"url": "YOUR_WEBHOOK_SITE_URL",
	"isEnabled": true
}'

Take note of the id property value in the returned webhook object since you will need this in the next step. The response also includes a secret — returned only once, at creation — which you use to verify webhook signatures (Step 5), so store it somewhere safe now. You may also repeat step 1 to verify that the new webhook is visible in the list of all your webhooks.

Step 4. Trigger the Webhook

To trigger the webhook you need to receive a transaction. You can do this for real by running the Send Transaction tutorial again. Feel free to do so and verify that a transaction.received event is listed in your webhook.site page.

The API also contains an endpoint for triggering test events manually. We will use this endpoint, /orgs/:orgId/webhooks/trigger-test-event, to verify our registered webhook with the test event we added earlier. Make sure to use the webhook id you received in the previous step.

curl -X POST https://api.arratech.com/orgs/YOUR_ORGS_ID/webhooks/trigger-test-event \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "webhookIds": [
    "YOUR_WEBHOOK_ID"
  ],
  "testEventType": "webhooks.test",
  "payload": {
    "note": "This is an arbitrary test payload, not a real transaction contract",
    "sampleField": "any-value-you-choose"
  }
}'

The payload here is arbitrary — whatever you send is delivered unchanged as the test event's payload. It does not have to match, and is not validated against, the real transaction.received payload; it just lets you exercise your endpoint.

After making the call above go to your webhook.site page and verify that the event is received.

Step 5. Verify the Webhook Signature

Every webhook Arratech sends includes an x-arratech-webhook-sign header so your handler can confirm the request genuinely came from Arratech and wasn't altered in transit. On your webhook.site page, open the event you just received and note this header — that is the value you verify against.

The header value is an HMAC-SHA256 of the exact raw request body, keyed by the secret returned when you created the webhook in Step 3, encoded as lowercase hex with no sha256= prefix. webhook.site only displays requests, so you can't verify there — here is how you would do it in your own Node.js handler:

const crypto = require("crypto");

// Capture the RAW body bytes, e.g. with Express:
//   app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }));
app.post("/webhook", (req, res) => {
  const received = req.header("x-arratech-webhook-sign");
  const expected = crypto
    .createHmac("sha256", process.env.WEBHOOK_SECRET) // secret from webhook creation
    .update(req.rawBody)                              // raw bytes, not the re-serialized JSON
    .digest("hex");                                   // lowercase hex, no "sha256=" prefix

  const valid =
    received &&
    received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));

  if (!valid) return res.status(401).send("invalid signature");

  // Signature OK — handle the event, then acknowledge with any 2xx.
  res.sendStatus(200);
});

Two things matter for a correct check: hash the raw bytes (re-serializing the JSON can reorder keys or change whitespace and break the match), and compare using a constant-time function like crypto.timingSafeEqual rather than ===.

Delivery and retries

Acknowledge each delivery by responding with any 2xx status. Anything else — or no response within 15 seconds — counts as a failed attempt and is retried up to two more times (three attempts total). Because delivery is at-least-once, your endpoint may occasionally receive the same event more than once; dedupe on the event's id, which stays the same across every retry.

See the Webhooks API reference for the full payload schema and signature details.

Step 6. List Earlier Webhook Events

In the case you need to retrieve the event we triggered above in retrospect you use the /orgs/:orgId/webhooks/events endpoint.

curl -X GET https://api.arratech.com/orgs/YOUR_ORGS_ID/webhooks/events \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Verify that the returned response contains the previously triggered event.

Congratulations! You have registered a webhook to be notified when you receive transactions.