Assay

Integrations

Alerts in by file or API. Events out by signed webhook. Your model, your key.

Assay sits between your transaction-monitoring system and the place your team works cases. Here is what connects today, and what is on the roadmap. Planned items are labeled planned; none of them works yet.

Available now

In the code and in the demo or server edition today.

CSV import

One row per transaction, grouped by alert_id. Up to 200 alerts a file, validated before anything is stored. Download the column template from the import screen.

Available now

REST API

POST /api/v1/alerts with the alert, customer and transactions; the response carries the recommendation. Bearer keys per workspace. API reference

Available now

Outbound webhook

Events as plain JSON, a Slack message or a Microsoft Teams card, signed with X-Assay-Signature, an HMAC-SHA256 of the raw body.

Available now

Anthropic

Claude models through the Anthropic API, with your workspace's own key. Selected per workspace in policy settings.

Available now

OpenAI

OpenAI models through the OpenAI API, with your own key. Same prompt, same validation, same policy rules as every other provider.

Available now

Azure OpenAI

Your own deployment in your own Azure subscription. The default for Enterprise, so prompts go to an endpoint you control.

Available now

On the roadmap

Built in the order design partners need them. Product names belong to their owners; no affiliation or endorsement implied.

NICE Actimize

Pull alerts from the Actimize queue and write the recommendation and cited rationale back to the case.

Planned, not available

Nasdaq Verafin

Bring Verafin alerts in for first-pass triage and return the disposition with its evidence trail.

Planned, not available

Hummingbird

Open a Hummingbird case on escalation, carrying the draft SAR narrative and cited records.

Planned, not available

Unit21

Triage Unit21 alerts and sync dispositions back, for fintechs and sponsor-bank programs already on it.

Planned, not available

Microsoft Sentinel

Stream the audit log, including policy changes and locked alerts, into Sentinel for security monitoring.

Planned, not available

Dynamics 365 and Dataverse

Write alerts, runs and decisions to Dataverse tables for teams that report from Dynamics 365 or Power BI.

Planned, not available

Outbound webhooks

An escalation, as your system receives it.

A POST with Content-Type: application/json and an X-Assay-Signature: sha256=<hex> header, where the hex is an HMAC-SHA256 of the raw request body under your webhook secret. Verify it before you trust the event.

Example alert.escalated payload. Synthetic alert.

{
  "event": "alert.escalated",
  "created_at": "2026-10-06T14:32:08Z",
  "alert": {
    "id": "ALT-7Q2M4K",
    "rule_code": "CASH-STRUCT-01",
    "typology": "structuring",
    "recommendation": "escalate",
    "confidence": 0.91,
    "risk_score": 82,
    "cited_claims": 6,
    "url": "https://YOUR-DEPLOYMENT/app/alerts/ALT-7Q2M4K"
  }
}

Verifying the signature in Node.js. Compare in constant time, and hash the bytes you received, not re-serialized JSON.

import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";

// Sign over the exact bytes received, so keep the raw body.
function verifyAssay(rawBody, header, secret) {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(header ?? "");
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

const app = express();
app.post("/hooks/assay", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyAssay(req.body, req.get("X-Assay-Signature"), process.env.ASSAY_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }
  const event = JSON.parse(req.body.toString("utf8"));
  // event.event === "alert.escalated": open a case, page the L2 queue, etc.
  res.status(204).end();
});

Slack and Teams do not check signatures. For those formats, the webhook URL is the secret: keep it out of tickets and screenshots.

Try it

Send a sample escalation to your own channel.

Paste a Slack or Teams incoming-webhook URL and the page posts a sample escalation for the demo's structuring alert, in that service's format. It goes straight from your browser; nothing passes through Assay.

Send a test escalation to your channel

The URL stays in this page's memory. It is not saved, logged or sent anywhere except to Slack or Microsoft, straight from your browser.

Format
Exact JSON that will be sent (Slack Block Kit)
{
  "text": "ALT-7Q2M4K (structuring) was escalated to L2. Test message, synthetic data.",
  "blocks": [
    {
      "type": "header",
      "text": {
        "type": "plain_text",
        "text": "Escalated to L2: ALT-7Q2M4K"
      }
    },
    {
      "type": "section",
      "fields": [
        {
          "type": "mrkdwn",
          "text": "*Typology*\nStructuring"
        },
        {
          "type": "mrkdwn",
          "text": "*Recommendation*\nEscalate"
        },
        {
          "type": "mrkdwn",
          "text": "*Confidence*\n0.91"
        },
        {
          "type": "mrkdwn",
          "text": "*Risk score*\n82 of 100"
        },
        {
          "type": "mrkdwn",
          "text": "*Cited claims*\n6"
        },
        {
          "type": "mrkdwn",
          "text": "*Rule*\nCASH-STRUCT-01"
        }
      ]
    },
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "Nine cash deposits from $9,400 to $9,900 at three locations over 12 days, including two days with deposits at two different locations. A staff note records the customer asking whether deposits over $10,000 are reported."
      }
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": {
            "type": "plain_text",
            "text": "Open in Assay"
          },
          "url": "/assay-aml-alert-triage/demo/"
        }
      ]
    },
    {
      "type": "context",
      "elements": [
        {
          "type": "mrkdwn",
          "text": "Test message sent from the Assay integrations page. Synthetic data, no real customer."
        }
      ]
    }
  ]
}
One POST, from your browser, nothing else.

CSV template

24 columns, one row per transaction.

Rows with the same alert_id become one alert. The 9 columns in bold are required; the rest make the evidence better. Larger volumes go through the API.

  1. alert_id
  2. rule_code
  3. rule_description
  4. typology
  5. alert_created_at
  6. customer_id
  7. customer_name
  8. customer_type
  9. occupation
  10. country
  11. onboarded_at
  12. risk_rating
  13. expected_monthly_volume
  14. kyc_notes
  15. txn_id
  16. txn_timestamp
  17. amount
  18. direction
  19. channel
  20. counterparty_name
  21. counterparty_country
  22. location
  23. memo
  24. triggered

Need a connector that is not built yet?

Design partners set the order of the roadmap. Tell me which case manager and monitoring system you run, and I will tell you honestly how far off it is.