J.Jupid Docs
Embed for partners

Webhooks

Send legacy Always Bank account snapshots to Jupid.

Use this webhook to send account-list changes from an existing Always Bank embed integration to Jupid. Your backend sends the event; Jupid receives it. It does not send transaction-processing notifications back to your backend.

For new integrations that upload accounts and transactions into a shared business, use the HTTP API. This webhook uses the legacy account snapshot flow and does not select a business or create an API source.

Jupid supplies a webhook URL and HMAC secret for each enabled environment. Keep the secret on your backend and send requests from there.

Account changes

The supported event is accounts.changed. Send it when a user's legacy Always Bank account list changes.

Use the same stable sub as the user's embed JWT. Complete a legacy embed login before sending snapshots so Jupid has the partner-user mapping. The snapshot applies to that user's legacy Always Bank accounts; business.id is descriptive metadata and does not route data to a shared business.

POST https://backend.jupid.com/api/webhooks/alwaysbank
Content-Type: application/json
x-jupid-signature: <hex hmac>

The example uses the production endpoint. For staging, use the webhook URL provided by Jupid.

Request body

Send the full current account list for this user, including accounts that have not changed:

{
  "type": "accounts.changed",
  "sub": "stable-partner-user-id",
  "payload": {
    "tier": "paid",
    "business": {
      "id": "business-123",
      "name": "Acme LLC"
    },
    "accounts": [
      {
        "id": "account-123",
        "name": "Operating Checking",
        "number": "****1234",
        "type": "checking",
        "currency": "USD",
        "current_balance": 12000.34,
        "plaid_token": "<optional-partner-provided-plaid-token>",
        "status": "active"
      }
    ]
  }
}

This is the legacy account-list payload from Authentication, not the HTTP API's business payload.

FieldRequirement
typeMust be accounts.changed.
subNonempty stable partner user ID.
payload.tierRequired: free or paid. This webhook does not update billing entitlement.
payload.businessOptional. If present, both nonempty id and name are required.
payload.accountsRequired array. An empty array marks all this user's legacy Always Bank accounts inactive.
Account id, name, currencyRequired; ID and name are nonempty strings, currency is a three-character string.
Account number, type, plaid_tokenOptional strings.
Account current_balanceOptional number in currency units. Omission preserves an existing balance; a new account starts at zero.
Account statusOptional: active or disabled.

Accounts omitted from a later snapshot are marked inactive. Their transaction history is retained. This is a full-list update, so do not send separate partial lists for different businesses under the same sub.

An enabled account with a plaid_token can use Jupid's Plaid synchronization flow. Omit the token only when the account should have no usable Plaid connection: omission clears a previously supplied token. The webhook itself does not accept transaction records.

Signatures

Set x-jupid-signature to a hex HMAC-SHA256 digest of the raw request body, using the webhook secret supplied for this environment. This is separate from the embed JWT signing secret.

import { createHmac } from "node:crypto"

const signature = createHmac("sha256", webhookSecret)
  .update(rawRequestBody)
  .digest("hex")

Use the exact bytes sent over HTTP as rawRequestBody. Reformatting JSON after signing changes the digest.

Responses

StatusMeaning
200{ "received": true }: the event was submitted for background processing.
401JSON error missing_signature or invalid_signature. Check the environment secret and signed bytes before retrying.
5xxSubmission or server failure. Invalid JSON or a body that fails validation also currently produces a server error.

The response does not confirm that accounts were updated or that Plaid synchronization completed. A missing partner-user mapping is detected in the background, after the webhook has already returned 200, and the snapshot is skipped.

Retry network failures and temporary server failures with backoff. For persistent failures, check the body against the schema above and contact Jupid. Repeating a snapshot uses the same account identities; it does not provide delivery ordering or a completion receipt.

Timing

Send accounts.changed after account-list changes and keep the latest full snapshot available for retries. If an event was sent before the first legacy embed login, send the latest snapshot again after login succeeds.

There is no snapshot version or ordering guarantee. An older event processed later can overwrite newer data or mark newer accounts inactive. Avoid replaying superseded snapshots; a 200 response alone does not establish processing order.

On this page