Skip to main content
This guide covers the full integration surface of Transaction Monitoring. The quickstart gets you running; this page gets you to production.

Architecture

Authentication

Standard x-api-key header. Same keys used for sessions and entities. See API authentication.

Submitting a transaction

Single endpoint: POST /v3/transactions/. See submitting transactions for the full schema. Minimal payload:
{
  "txn_id": "tx-0001",
  "direction": "OUTBOUND",
  "amount": "500.00",
  "currency": "EUR",
  "applicant": { "vendor_data": "user-42" },
  "counterparty": { "name": "Acme Corp" }
}
Richer payload with party modeling, payment methods, device context:
{
  "txn_id": "tx-0002",
  "txn_date": "2026-04-16T10:00:00Z",
  "direction": "OUTBOUND",
  "amount": "12500.00",
  "currency": "USD",
  "transaction_type": "finance",
  "applicant": {
    "vendor_data": "user-42",
    "full_name": "Jane Doe",
    "country_code": "USA",
    "device": { "ip": "203.0.113.5", "user_agent": "..." }
  },
  "counterparty": {
    "vendor_data": "biz-acme",
    "country_code": "USA"
  },
  "payment_methods": [
    {
      "role": "SOURCE",
      "type": "BANK_ACCOUNT",
      "fingerprint": "bank:us:chase:****1234",
      "issuing_country": "USA"
    },
    {
      "role": "DESTINATION",
      "type": "BANK_ACCOUNT",
      "fingerprint": "bank:us:boa:****5678",
      "issuing_country": "USA"
    }
  ],
  "custom_values": {
    "invoice_id": "INV-2026-00042",
    "category": "b2b_goods"
  }
}

Party modeling

Transactions support up to four parties:
RoleWho
APPLICANTThe customer on your platform initiating the transaction
REMITTERWhoever sent the funds (often same as applicant)
BENEFICIARYWhoever receives the funds (often same as counterparty)
COUNTERPARTYThe other side of the transaction
Each party can carry vendor_data (binding to a User or Business entity), or be included as a snapshot (external, no entity). See transactions payload for party fields.

Idempotency

txn_id is unique per application. Re-submitting the same txn_id returns the existing transaction’s current state (not an error) — effectively idempotent for retries. Always generate txn_id on your side before submitting; don’t rely on a Didit-generated ID for idempotency. Use your own database ID, a UUID, or a hash of transaction fields.

Real-time vs batch

  • Real-time — recommended for most use cases. Submit the transaction the moment it happens in your system. Rules evaluate synchronously and return the decision.
  • Batch / backfill — for historical ingestion, pass txn_date in the past. Rules still run and can trigger webhooks. To avoid triggering rules on backfill, use the console’s CSV import with the “dry ingest” flag.

Historical ingestion

When you’re migrating from another transaction monitoring system:
  1. Export your historical transactions to a CSV.
  2. Use Transactions → Import history in the console (or submit via API in a loop) with txn_date in the past.
  3. Enable rules only after ingestion finishes, or use dry ingest to skip rule evaluation.
Be deliberate about historical rule evaluation. Feeding 6 months of history through a “20 transfers in 30 days” rule will generate thousands of alerts at once.

Webhooks

Subscribe to:
  • transaction.created — every submitted transaction.
  • transaction.status.updated — status changes (e.g. analyst moves from IN_REVIEW to APPROVED, or an async AML check updates the score).
Full details: TM webhooks.
app.post('/webhooks/didit-tm', (req, res) => {
  if (!verifySignature(req)) return res.status(401).end();

  const { event, data } = req.body;
  if (event === 'transaction.status.updated' && data.status === 'DECLINED') {
    suspendTransaction(data.txn_id);
  }
  res.status(200).end();
});

Handling statuses

StatusWhat to do
APPROVEDProcess the transaction as normal.
IN_REVIEWHold the transaction until analyst review or automated re-check resolves.
DECLINEDReject the transaction. Surface a generic error to the user.
AWAITING_USERA remediation session has been created — deliver its URL to the user.
See TM statuses.

Linking transactions to entities

Pass vendor_data on every party. Benefits:
  • Every transaction aggregates into the User or Business profile.
  • Velocity and behavioral rules have the full history to evaluate against.
  • Blocking an entity propagates to future transactions automatically.
Missing vendor_data isn’t a hard error — the transaction is stored as an external snapshot — but you lose entity-level aggregation.

Remediation sessions

When a rule action moves a transaction to AWAITING_USER, Didit automatically creates a remediation session linked to the same vendor_data. The response includes a remediation_session_id and a url you deliver to the user:
{
  "status": "AWAITING_USER",
  "remediation_session_id": "sess_...",
  "remediation_session_url": "https://verify.didit.me/..."
}
The user completes additional verification (e.g. source of funds, refreshed ID). On completion, the transaction status updates to APPROVED or DECLINED automatically, and a transaction.status.updated webhook fires.

Best practices

PracticeWhy
Always pass vendor_dataEnables aggregation.
Use your own txn_idIdempotency.
Subscribe to both transaction.created and transaction.status.updatedCatch async state changes (e.g. analyst review outcomes).
Set rule thresholds deliberatelyLow thresholds = more alerts; high = more missed risk. See risk scoring.
Monitor rule run counts in consoleCatch rules that never fire (misconfigured) or fire on everything (too loose).
Test rules in TEST mode before going liveSee rules.

Rate limits

See rate limiting. Transaction create is one of the highest-throughput endpoints; typical limits are in the hundreds of requests per second. Burst carefully.

Next steps

Transactions

Full payload reference.

Rules

How rules evaluate.

Webhooks

Event catalog.

Troubleshooting

Common integration issues.