Gareth Wilson Gareth Wilson

Guide to Arcadia Webhooks: Features and Best Practices

Published


Arcadia webhooks tell your systems when something changes in the utility data Arcadia collects for you. A credential starts failing to log in, a new bill statement is discovered, interval data arrives for a meter, or a user is asked for a multi-factor authentication code. If you build on Arcadia's Plug API, webhooks replace polling and let you fetch, store or act on new data as soon as Arcadia has it.

This guide covers how Arcadia routes webhooks through business units, the event types and payload shape, how to authenticate requests with a custom header that Arcadia support configures, how retries work, and the practices that keep an Arcadia receiver reliable.

What are Arcadia webhooks?

Arcadia sends a JSON POST to the webhook URLs you configure on a business unit. Each business unit can have a live URL and a sandbox URL. When an entity changes, Arcadia sends the webhook to every business unit related to it and to all of those business units' ancestors, if they have a URL configured. Each URL gets its own webhook with a distinct id, and the payloads are otherwise identical.

There is no per-event subscription. Every URL receives every event type, and Arcadia's own advice is to filter on the top-level resource and eventType fields and acknowledge the rest with a 2xx.

Arcadia doesn't sign webhooks. The only authentication it documents is a set of custom headers or query parameters sent with every webhook, and you can't set those yourself: Arcadia support configures them for your organisation on request.

Arcadia webhook features

FeatureDetails
ConfigurationArcadia Dashboard, Business units > a business unit > Webhooks panel
URLsA live URL and a sandbox URL per business unit
Fan-outSent to related business units and their ancestors, one webhook id per URL
Event selectionNone. Every URL receives every event type
Method and formatPOST, JSON
Event fieldeventType, with the entity type in resource
AuthenticationCustom headers or query parameters set by Arcadia support
Header limitsName up to 80 characters, value up to 255. Alphanumerics, _ and -; values also allow /, ., \ and ,. No spaces
Signature and timestampNone
Response windowA 2xx within 10 seconds after the connection is made
RetriesThree times every six hours for a day, twelve retries at most, with an identical body
RTCV retriesAt most three
ResendingAPI endpoints list and resend stored webhooks
Source IPsNot published

Event types

Arcadia's event types span several resources, so a router needs both eventType and resource to know what happened.

eventTyperesource valuesSent when
STATUS_CHANGECREDENTIAL, ACCOUNT, FILE, DELETION, DOWNLOAD, METERA status or status detail changes, including account and meter voiding
NEW_DATA_AVAILABLESTATEMENT, DISCOVERED_STATEMENT, METERA statement is discovered for the first time, or a new meter is discovered
EXISTING_DATA_UPDATEDSTATEMENT, DISCOVERED_STATEMENT, METERA stored statement changes, or new interval data is available for a meter
REAL_TIME_CREDENTIAL_VALIDATION_SUCCESSCREDENTIALArcadia logged in to the utility account, for credentials created with isInteractive: true
REAL_TIME_CREDENTIAL_VALIDATION_FAILURECREDENTIALArcadia failed to log in, for credentials created with isInteractive: true
MFA_CHALLENGE_METHODMFA_METHOD_PROMPTThe user has to choose an MFA method
MFA_CHALLENGE_CODEMFA_CODE_PROMPTThe user has to enter an MFA code
BUSINESS_UNIT_ASSIGNMENTROOT_BUSINESS_UNIT_ACCOUNTAn account is assigned to the root business unit. Sent only to the root business unit's URL
DATA_DELETEDBUSINESS_UNIT_DELETIONA user deletes a business unit

The two MFA types appear in Arcadia's examples and scenarios rather than in its event tables. They are part of the interactive "Connect" flow, together with the real-time credential validation (RTCV) events.

Arcadia treats an adjustment statement as a new statement with its own ID, so it arrives as NEW_DATA_AVAILABLE. When Arcadia improves its extraction logic, a statement can be re-sent as EXISTING_DATA_UPDATED with the same statement ID and slightly different data.

Payload structure

Every webhook has the same envelope. Here is a trimmed STATUS_CHANGE for a credential, from Arcadia's examples:

{
  "businessUnitIds": ["bus_b2d4e1d1-3028-becd-1821-ff80aa8107f5"],
  "createdAt": "2024-02-21T00:00:00.000+00:00",
  "data": {
    "id": "crd_0c5d396f-f11c-c76b-9be8-89343463af21",
    "connectionHealth": "CONNECTION_SUCCESS",
    "status": "CONNECTION_SUCCESS",
    "statusDetail": "LOGIN_AND_DATA_DISCOVERY_SUCCESS",
    "totalAccounts": 1
  },
  "errors": [],
  "eventType": "STATUS_CHANGE",
  "id": "whk_9ec2fbdc-33ff-13b2-9b97-47d9ee7dcc73",
  "resource": "CREDENTIAL"
}

data holds the same representation the API returns for that resource, so you often don't need a follow-up call. For statement events, it matches the summarised statement endpoint, not the detailed one. businessUnitIds lists only the directly related business units, not the ancestors that also received the webhook.

Inspect Arcadia webhooks as they arrive. Set a Hookdeck Inspect URL as a sandbox webhook URL on a business unit to see the exact headers and body Arcadia sends, with no account or setup required.

Setting up Arcadia webhooks

Pick a header name and a long random value first. Because spaces aren't allowed, the value can't be Bearer <token>, so use a custom header such as X-Arcadia-Token with a bare token, for example from openssl rand -hex 32. That keeps the value within Arcadia's character set.

  1. Open the Arcadia Dashboard, go to Business units and open the business unit's details.
  2. Edit the Webhooks panel and enter your HTTPS endpoint as the live or sandbox webhook URL.
  3. Open a support ticket with Arcadia through Zendesk and ask them to add your header name and value to your webhooks. Arcadia sets these values internally; they can't be set through the API or the Dashboard.
  4. Wait for support to confirm, then trigger an event, for example by creating a test credential in sandbox, and check that the header arrives.

The header applies to every business unit in your organisation, so one value covers all your URLs. Arcadia can also add query parameters, but a header is the better choice: query strings end up in access logs.

With Hookdeck. Arcadia is a supported source in Hookdeck. Create a source with the Arcadia type and use its URL as the webhook URL. Enter the header name you asked for in API Key Header and its exact value in API Key. Header names match case-insensitively; the value must match exactly. The source accepts POST only, which is what Arcadia sends. It checks the header, not a query parameter, so ask support for a header. A request with a missing or wrong value still gets a 200 from Hookdeck, but Hookdeck marks it as failed verification and doesn't deliver it to your service.

For local development, hookdeck listen 3000 arcadia --path /webhooks/arcadia gives you a public URL that forwards to your machine.

Securing Arcadia webhooks

With no signature, verification means reading the header Arcadia support configured and comparing its whole value with the one you stored, in constant time. Reject the request if the header is missing or the value isn't configured.

const crypto = require('crypto');
const express = require('express');

const HEADER_NAME = (process.env.ARCADIA_WEBHOOK_HEADER || 'x-arcadia-token').toLowerCase();
const SECRET = process.env.ARCADIA_WEBHOOK_SECRET;

function safeEqual(received, expected) {
  const a = Buffer.from(received, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  // timingSafeEqual throws on unequal lengths, so check first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();

app.post('/webhooks/arcadia', express.raw({ type: '*/*' }), (req, res) => {
  const received = req.get(HEADER_NAME); // case-insensitive lookup
  if (!SECRET || typeof received !== 'string' || !safeEqual(received, SECRET)) {
    return res.sendStatus(401);
  }

  let event;
  try {
    event = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }

  // Route on the pair; acknowledge anything you don't handle.
  const key = `${event.eventType}:${event.resource}`;
  if (key === 'NEW_DATA_AVAILABLE:STATEMENT') {
    // Queue a job to store the new statement in event.data.
  }

  res.sendStatus(200);
});

The same check in Python:

import hmac
import os

HEADER_NAME = os.environ.get("ARCADIA_WEBHOOK_HEADER", "X-Arcadia-Token")
SECRET = os.environ.get("ARCADIA_WEBHOOK_SECRET", "")


def verify_arcadia_request(headers) -> bool:
    if not SECRET:
        return False  # never accept when unconfigured
    received = headers.get(HEADER_NAME)  # Starlette and Flask headers are case-insensitive
    if not received:
        return False
    # Compare bytes: compare_digest raises TypeError on a non-ASCII str.
    return hmac.compare_digest(received.encode("utf-8"), SECRET.encode("utf-8"))

A static header proves only that the sender knows the value. It doesn't cover the body, so a modified payload still passes, and there is no timestamp, so a captured request can be replayed. Use HTTPS only, keep the value out of logs, and ask Arcadia support to change it if it might have leaked.

Make Arcadia webhooks production-ready. Hookdeck Event Gateway checks the header Arcadia sends, answers within Arcadia's 10-second window, and queues every webhook so you can retry and replay deliveries on your own schedule.

Arcadia webhook limitations and pain points

You can't set or rotate the secret yourself

The Problem: Adding authentication, or changing a leaked value, takes a support ticket and a wait.

Why It Happens: Arcadia sets custom headers and query parameters internally. They can't be changed through the API or the Dashboard.

Workarounds:

  • Ask for the header when you first set up webhooks, not later.
  • To rotate, accept both the old and the new value in your receiver until Arcadia confirms the change, then drop the old one.

How Hookdeck Can Help: Hookdeck's Arcadia source checks the header for you and keeps every request, including the ones that fail verification, with full headers, so you can see which value Arcadia is sending during a rotation.

Every URL gets every event

The Problem: A service that only cares about new statements still receives credential status changes, MFA prompts and meter updates.

Why It Happens: Arcadia doesn't support opting in or out of event types, and the fan-out to ancestor business units means a parent URL also sees its children's events.

Workarounds:

  • Route on eventType and resource and return a 2xx for anything you don't handle, as Arcadia recommends.
  • Use businessUnitIds to tell which business unit an event belongs to.

How Hookdeck Can Help: Hookdeck filters can route on eventType and resource in the body, so each of your services only receives the events it handles.

Ten seconds, then hours of waiting

The Problem: A slow handler or a short outage turns into a delay of six hours or more.

Why It Happens: Arcadia waits 10 seconds for a 2xx after connecting. A failed delivery is retried three times every six hours for a day, and RTCV webhooks only three times in total.

Workarounds:

  • Acknowledge first and process asynchronously, as Arcadia recommends for anything longer than 10 seconds.
  • Use Arcadia's endpoints to list and resend stored webhooks after an outage.

How Hookdeck Can Help: Hookdeck answers Arcadia as soon as it has stored the request, then delivers to your service with retries you configure. You can replay any event after a fix without waiting for Arcadia's next attempt.

Duplicates across business units

The Problem: The same change can reach your system several times.

Why It Happens: One webhook is created per URL, so a child and a parent business unit pointing at the same service each get a copy with a different id. Retries resend the same body, with the same id.

Workarounds:

  • Deduplicate retries on id.
  • Deduplicate fan-out copies on the resource: resource, data.id and eventType, plus a field that changes between real updates, such as the status.

How Hookdeck Can Help: Hookdeck's deduplication can drop repeats based on fields you choose, before they reach your service.

Best practices

Ask for a header, not a query parameter

Arcadia can add either, but URLs are logged in many more places than headers. A custom header with a bare token, such as X-Arcadia-Token, fits Arcadia's character rules and is what Hookdeck's source checks.

Treat data as the API representation

data matches what the API returns for the resource, so you can usually store it without another call. Remember that statement webhooks carry the summarised statement; fetch the detailed statement if you need line items.

Make handlers idempotent

Store id before acting and skip repeats. For statements, key on the statement ID, since EXISTING_DATA_UPDATED re-sends a known statement with revised values. See our guide to webhook idempotency.

Acknowledge fast and process asynchronously

Verify the header, store the body, return 200, and do the work in a background job. See why to process webhooks asynchronously.

Handle the interactive flow promptly

If you build your own Connect experience, the MFA and RTCV events drive what the user sees next. Push them to the user's session quickly, and don't rely on retries: RTCV webhooks are retried at most three times.

Conclusion

Arcadia sends every event type to every webhook URL on a business unit and its ancestors, as JSON with eventType, resource and the API representation of the resource in data. There is no signature, so ask Arcadia support for a custom header with a long random value, compare it in full and in constant time, route on eventType and resource, and deduplicate on id and the resource.

Hookdeck Event Gateway checks that header, answers within Arcadia's 10-second window, and lets you filter, retry and replay events, so an outage on your side doesn't leave you waiting six hours for Arcadia's next attempt.

Get started with Hookdeck for free and handle Arcadia webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.