Introduction
A new class of payment fraud is emerging that classic risk engines were never designed to catch. In agentic commerce, autonomous AI agents place orders, sign mandates, and move money on behalf of users. The convenient corollary is that fraudsters can spin up agents too, and when several of those agents coordinate, each one stays under the per-agent velocity caps while the ring as a whole drains a merchant or a card portfolio in seconds.
A single agent buying a $250 gift card is unremarkable. Three agents, owned by three different "users", buying $250 gift cards at the same merchant within forty seconds, all signed by the same mandate authority, all funded by the same card, and all running on the same device fingerprint, is a coordinated attack. The per-agent fraud model sees three boring transactions. The cross-agent model sees one ring.
This is the signature problem of multi-agent collusion fraud, and it requires a different shape of detection: cross-agent correlation that joins signals across agents, users, devices, signers, funding sources, and merchants in real time. This tutorial walks through building that detection pipeline in streaming SQL using RisingWave, with every query verified against RisingWave v2.8.0 and the actual outputs embedded in the post. By the end, you will have a composite collusion score that automatically classifies every agent as BLOCK, REVIEW, or ALLOW, and you will see why per-agent rules cannot replace this kind of analysis.
What Is Multi-Agent Collusion?
Multi-agent collusion is the coordinated use of two or more autonomous commerce agents to execute a fraudulent pattern that no single agent would trigger on its own. The agents may belong to the same operator running a fleet of synthetic users, or to a ring of operators sharing infrastructure such as device fingerprints, payment instruments, or mandate signers.
Agentic commerce platforms such as those described in the Agentic Commerce Protocol and Visa's recent work on agentic payments typically issue a per-agent mandate, scope the mandate by amount and merchant, and sign the agent's transactions on behalf of a user. The mandate model is excellent at constraining a single agent. It is much weaker at constraining a coordinated set of agents, because each agent is a valid signed entity in isolation.
Collusion attacks exploit three asymmetries:
- Per-agent velocity rules. A rule like "no agent may spend more than $1,000 per minute" caps a single agent. Ten agents spending $999 per minute together drain $10,000 per minute and pass every per-agent rule.
- Per-account spend caps. A user-level cap protects one account. A ring spinning up fifty thin synthetic accounts, each with a small cap, sums to a large attack surface.
- Per-mandate scope. A mandate scoped to a single merchant looks safe individually, but a coordinated burst of fifty mandates pointing at the same merchant inside a one-minute window is a different threat altogether.
Closing these gaps requires looking at the population of agents simultaneously, not at each agent in isolation. That is what cross-agent correlation does, and it is what streaming SQL is naturally good at.
Common Collusion Patterns
Three coordination patterns show up repeatedly in real incidents and red-team exercises:
- Cloned-agent rings. A single operator clones an agent, gives each clone a different user id, and runs them in parallel. Device fingerprint, mandate signer, and funding source are shared. The ring drains a target merchant in seconds before any per-account limit kicks in.
- Synthetic identity collusion. A ring stitches together synthetic identities, each with its own device, but the agents reuse the same payment instrument or the same mandate authority. Detection here hinges on funding-source overlap and signer reuse rather than device sharing.
- Burst convergence. Independent operators coordinate via an out-of-band channel and converge on the same merchant or SKU at a precise time, for example to drain limited drops, exploit price-glitch windows, or chargeback-cycle a merchant. Device, signer, and funding may all differ; the time correlation is the only signal.
Each pattern leaves a different fingerprint in the data. A general detection pipeline must score multiple signals and combine them, because no single signal is sufficient on its own.
Why Per-Agent Rules Miss Collusion
Per-agent fraud rules ask "is this agent behaving badly?" Collusion fraud asks "are these agents behaving badly together?" The two questions have different math.
Consider the cloned-agent example again. Each clone fires three transactions, well below the typical per-agent rate threshold. Each clone sits inside a separate user account with a clean history. Each transaction is signed by a valid mandate. From the per-agent perspective, every box is checked.
The pattern only becomes visible when you join across agents. The clones share a device fingerprint. They burst within seconds of each other. They share a funding card. The mandate signer is identical. None of those facts are visible inside the per-agent state machine, because per-agent state machines do not look outside the agent.
The shape of this problem is identical to classic fraud-ring detection in card-not-present commerce, but the timescales are tighter. An agent ring can complete in seconds. A batch ETL job that aggregates by device or signer every fifteen minutes will detect the ring, but only after the money has moved. You need the join to happen continuously as data arrives.
For more background on related real-time detection problems, see our previous tutorials on payment fraud detection with streaming SQL and account takeover detection in real time.
Cross-Agent Correlation Signals
Five signals carry most of the detection weight in our experience. Each is cheap to compute incrementally and can be expressed as a streaming materialized view.
Signal 1: shared device fingerprint across users
A device fingerprint that hosts agents belonging to two or more distinct user ids is a strong indicator of cloning or shared infrastructure. The exception is a single human running multiple agents from one laptop, which is why the rule keys on COUNT(DISTINCT user_id) >= 2, not just COUNT(DISTINCT agent_id) >= 2.
Signal 2: time-correlated bursts at the same merchant
A tumbling window over the transaction stream, grouped by merchant, surfaces sub-minute clusters where many distinct agents converge on the same merchant. A burst of three or more distinct agents inside a sixty-second window at the same merchant is rarely organic.
Signal 3: mandate signer reused across users
In agentic payment protocols, the mandate signer is the entity authorizing the agent to act. Legitimately, one signer per user. When a single signer authorizes mandates for many user identities, it points at a synthetic-identity workshop or a compromised signing key.
Signal 4: funding source overlap
The same card or wallet funding agents that claim to belong to different users is one of the most reliable collusion signals. Even when devices and signers differ, funding rarely lies: the money has to come from somewhere, and rings often economize on instruments.
Signal 5: merchant or SKU cluster overlap
Independent agents converging on the same merchant in a short window is a weaker signal but useful as a tiebreaker. Most agents shop across many merchants. A cluster pointing at one is suspicious, especially when combined with any of the four signals above.
The five signals are deliberately heterogeneous: device, time, signer, funding, merchant. A ring usually masks one or two but rarely all five.
Detecting Collusion with Streaming SQL
This section builds the pipeline end-to-end on a running RisingWave instance. Every query was executed against RisingWave v2.8.0 and the outputs are the raw query results. The schema and views are namespaced with the aap08_ prefix so you can drop them cleanly when you finish.
Step 1: schema and sample data
Create the agent transaction table:
CREATE TABLE aap08_agent_tx (
tx_id VARCHAR PRIMARY KEY,
agent_id VARCHAR NOT NULL,
user_id VARCHAR NOT NULL,
device_fingerprint VARCHAR NOT NULL,
mandate_signer VARCHAR NOT NULL,
funding_source VARCHAR NOT NULL,
merchant VARCHAR NOT NULL,
amount DECIMAL NOT NULL,
tx_time TIMESTAMPTZ NOT NULL
);
In production, this table is replaced by a Kafka source consuming agent-tagged transaction events from your gateway. Insert thirty representative transactions that embed the four collusion patterns plus negative cases:
INSERT INTO aap08_agent_tx VALUES
-- Cluster A: 3 agents sharing the same device, signer, and funding card
('tx_001', 'agent_a1', 'user_u100', 'dev_zeta_77', 'sig_alpha', 'card_visa_4001', 'merch_electronics', 199.00, '2026-05-06 09:00:01+00'),
('tx_002', 'agent_a2', 'user_u101', 'dev_zeta_77', 'sig_alpha', 'card_visa_4001', 'merch_electronics', 215.50, '2026-05-06 09:00:08+00'),
('tx_003', 'agent_a3', 'user_u102', 'dev_zeta_77', 'sig_alpha', 'card_visa_4001', 'merch_electronics', 188.75, '2026-05-06 09:00:15+00'),
('tx_004', 'agent_a1', 'user_u100', 'dev_zeta_77', 'sig_alpha', 'card_visa_4001', 'merch_giftcard', 250.00, '2026-05-06 09:00:25+00'),
('tx_005', 'agent_a2', 'user_u101', 'dev_zeta_77', 'sig_alpha', 'card_visa_4001', 'merch_giftcard', 250.00, '2026-05-06 09:00:32+00'),
('tx_006', 'agent_a3', 'user_u102', 'dev_zeta_77', 'sig_alpha', 'card_visa_4001', 'merch_giftcard', 250.00, '2026-05-06 09:00:40+00'),
-- Cluster B: 4 agents bursting at the same merchant within 60 seconds
('tx_007', 'agent_b1', 'user_u200', 'dev_b_801', 'sig_beta_1', 'card_mc_5001', 'merch_jewelry', 999.00, '2026-05-06 10:00:05+00'),
('tx_008', 'agent_b2', 'user_u201', 'dev_b_802', 'sig_beta_2', 'card_mc_5002', 'merch_jewelry', 999.00, '2026-05-06 10:00:18+00'),
('tx_009', 'agent_b3', 'user_u202', 'dev_b_803', 'sig_beta_3', 'card_mc_5003', 'merch_jewelry', 999.00, '2026-05-06 10:00:32+00'),
('tx_010', 'agent_b4', 'user_u203', 'dev_b_804', 'sig_beta_4', 'card_mc_5004', 'merch_jewelry', 999.00, '2026-05-06 10:00:51+00'),
-- Cluster C: 2 distinct user_ids signed by the same mandate authority
('tx_011', 'agent_c1', 'user_u300', 'dev_c_900', 'sig_gamma_x', 'card_amex_6001', 'merch_streaming', 14.99, '2026-05-06 11:05:00+00'),
('tx_012', 'agent_c2', 'user_u301', 'dev_c_901', 'sig_gamma_x', 'card_amex_6002', 'merch_streaming', 14.99, '2026-05-06 11:05:15+00'),
('tx_013', 'agent_c1', 'user_u300', 'dev_c_900', 'sig_gamma_x', 'card_amex_6001', 'merch_food', 32.50, '2026-05-06 11:30:00+00'),
('tx_014', 'agent_c2', 'user_u301', 'dev_c_901', 'sig_gamma_x', 'card_amex_6002', 'merch_food', 32.50, '2026-05-06 11:30:08+00'),
-- Cluster D: shared funding card across three nominally separate users
('tx_015', 'agent_d1', 'user_u400', 'dev_d_001', 'sig_delta_1', 'card_visa_7000', 'merch_travel', 540.00, '2026-05-06 12:00:00+00'),
('tx_016', 'agent_d2', 'user_u401', 'dev_d_002', 'sig_delta_2', 'card_visa_7000', 'merch_travel', 540.00, '2026-05-06 12:01:00+00'),
('tx_017', 'agent_d3', 'user_u402', 'dev_d_003', 'sig_delta_3', 'card_visa_7000', 'merch_travel', 540.00, '2026-05-06 12:02:00+00'),
-- Legitimate solo agents with no overlap
('tx_018', 'agent_x1', 'user_u500', 'dev_solo_1', 'sig_solo_1', 'card_visa_8001', 'merch_grocery', 87.20, '2026-05-06 08:15:00+00'),
('tx_019', 'agent_x1', 'user_u500', 'dev_solo_1', 'sig_solo_1', 'card_visa_8001', 'merch_pharmacy', 23.10, '2026-05-06 08:45:00+00'),
('tx_020', 'agent_x1', 'user_u500', 'dev_solo_1', 'sig_solo_1', 'card_visa_8001', 'merch_grocery', 64.40, '2026-05-06 13:00:00+00'),
('tx_021', 'agent_x2', 'user_u501', 'dev_solo_2', 'sig_solo_2', 'card_visa_8002', 'merch_books', 18.99, '2026-05-06 14:30:00+00'),
('tx_022', 'agent_x2', 'user_u501', 'dev_solo_2', 'sig_solo_2', 'card_visa_8002', 'merch_books', 22.50, '2026-05-06 15:00:00+00'),
('tx_023', 'agent_x3', 'user_u502', 'dev_solo_3', 'sig_solo_3', 'card_visa_8003', 'merch_clothing', 124.00, '2026-05-06 16:00:00+00'),
-- Legitimate user with two of their own agents on the same device
('tx_024', 'agent_x4a', 'user_u503', 'dev_legit_4', 'sig_legit_4', 'card_visa_8004', 'merch_grocery', 55.00, '2026-05-06 09:30:00+00'),
('tx_025', 'agent_x4b', 'user_u503', 'dev_legit_4', 'sig_legit_4', 'card_visa_8004', 'merch_pharmacy', 28.40, '2026-05-06 17:00:00+00'),
-- Cross-cluster funding overlap: cluster B agents reusing card_visa_7000
('tx_026', 'agent_b1', 'user_u200', 'dev_b_801', 'sig_beta_1', 'card_visa_7000', 'merch_jewelry', 800.00, '2026-05-06 10:30:00+00'),
('tx_027', 'agent_b2', 'user_u201', 'dev_b_802', 'sig_beta_2', 'card_visa_7000', 'merch_jewelry', 800.00, '2026-05-06 10:30:30+00'),
-- More benign noise
('tx_028', 'agent_x5', 'user_u504', 'dev_solo_5', 'sig_solo_5', 'card_visa_8005', 'merch_coffee', 6.50, '2026-05-06 07:30:00+00'),
('tx_029', 'agent_x6', 'user_u505', 'dev_solo_6', 'sig_solo_6', 'card_visa_8006', 'merch_clothing', 89.00, '2026-05-06 18:00:00+00'),
('tx_030', 'agent_x7', 'user_u506', 'dev_solo_7', 'sig_solo_7', 'card_visa_8007', 'merch_grocery', 42.00, '2026-05-06 19:00:00+00');
The dataset embeds four real collusion patterns and a deliberate decoy: user_u503 legitimately runs two of their own agents from a single device, which should not trigger the rule because only one user_id is involved.
Step 2: shared device fingerprint
CREATE MATERIALIZED VIEW aap08_shared_device_mv AS
SELECT
device_fingerprint,
COUNT(DISTINCT agent_id) AS distinct_agents,
COUNT(DISTINCT user_id) AS distinct_users,
COUNT(*) AS tx_count,
MIN(tx_time) AS first_seen,
MAX(tx_time) AS last_seen
FROM aap08_agent_tx
GROUP BY device_fingerprint
HAVING COUNT(DISTINCT agent_id) >= 2;
Querying the view returns:
device_fingerprint | distinct_agents | distinct_users | tx_count | first_seen | last_seen
--------------------+-----------------+----------------+----------+---------------------------+---------------------------
dev_zeta_77 | 3 | 3 | 6 | 2026-05-06 09:00:01+00:00 | 2026-05-06 09:00:40+00:00
dev_legit_4 | 2 | 1 | 2 | 2026-05-06 09:30:00+00:00 | 2026-05-06 17:00:00+00:00
Two devices show up. dev_zeta_77 has three agents owned by three distinct users, which is the cloned-agent ring. dev_legit_4 has two agents but only one user, which is benign multi-agent operation by a single human. The downstream scoring view will use distinct_users to draw that distinction.
Step 3: time-correlated bursts
A tumbling window over the transaction stream, grouped by merchant, captures sub-minute convergence:
CREATE MATERIALIZED VIEW aap08_time_correlated_mv AS
SELECT
merchant,
window_start,
window_end,
COUNT(DISTINCT agent_id) AS distinct_agents,
COUNT(DISTINCT user_id) AS distinct_users,
COUNT(*) AS tx_count,
SUM(amount) AS total_amount
FROM TUMBLE(aap08_agent_tx, tx_time, INTERVAL '60 seconds')
GROUP BY merchant, window_start, window_end
HAVING COUNT(DISTINCT agent_id) >= 3;
Output:
merchant | window_start | window_end | distinct_agents | distinct_users | tx_count | total_amount
-------------------+---------------------------+---------------------------+-----------------+----------------+----------+--------------
merch_electronics | 2026-05-06 09:00:00+00:00 | 2026-05-06 09:01:00+00:00 | 3 | 3 | 3 | 603.25
merch_giftcard | 2026-05-06 09:00:00+00:00 | 2026-05-06 09:01:00+00:00 | 3 | 3 | 3 | 750.00
merch_jewelry | 2026-05-06 10:00:00+00:00 | 2026-05-06 10:01:00+00:00 | 4 | 4 | 4 | 3996.00
The cloned-agent ring at cluster A surfaces twice (the ring hits two different merchants in two adjacent windows), and the burst-convergence ring at cluster B surfaces with four distinct agents inside a single sixty-second window at merch_jewelry. RisingWave's tumbling window function maintains the bucket aggregation incrementally as new events arrive.
Step 4: mandate signer reuse
CREATE MATERIALIZED VIEW aap08_shared_signer_mv AS
SELECT
mandate_signer,
COUNT(DISTINCT user_id) AS distinct_users,
COUNT(DISTINCT agent_id) AS distinct_agents,
COUNT(*) AS tx_count,
SUM(amount) AS total_amount
FROM aap08_agent_tx
GROUP BY mandate_signer
HAVING COUNT(DISTINCT user_id) >= 2;
Output:
mandate_signer | distinct_users | distinct_agents | tx_count | total_amount
----------------+----------------+-----------------+----------+--------------
sig_alpha | 3 | 3 | 6 | 1353.25
sig_gamma_x | 2 | 2 | 4 | 94.98
Both signers authorize multiple distinct user_ids, which is the synthetic-identity tell. sig_alpha is the cloned-agent ring's signer; sig_gamma_x is a milder case where two users share a signer for low-value subscription and food purchases. The lower-amount case will score lower in the composite, but it remains visible.
Step 5: funding source overlap
CREATE MATERIALIZED VIEW aap08_funding_overlap_mv AS
SELECT
funding_source,
COUNT(DISTINCT agent_id) AS distinct_agents,
COUNT(DISTINCT user_id) AS distinct_users,
COUNT(*) AS tx_count,
SUM(amount) AS total_amount
FROM aap08_agent_tx
GROUP BY funding_source
HAVING COUNT(DISTINCT user_id) >= 2;
Output:
funding_source | distinct_agents | distinct_users | tx_count | total_amount
----------------+-----------------+----------------+----------+--------------
card_visa_7000 | 5 | 5 | 5 | 3220.00
card_visa_4001 | 3 | 3 | 6 | 1353.25
card_visa_7000 funds five distinct user identities across two clusters, which is the strongest single ring signal in the dataset. Funding overlap is hard for ring operators to mask because they have to fund the activity from somewhere, and consolidating instruments saves money.
Step 6: merchant cluster overlap
CREATE MATERIALIZED VIEW aap08_merchant_overlap_mv AS
SELECT
merchant,
COUNT(DISTINCT agent_id) AS distinct_agents,
COUNT(DISTINCT user_id) AS distinct_users,
COUNT(*) AS tx_count
FROM aap08_agent_tx
GROUP BY merchant
HAVING COUNT(DISTINCT agent_id) >= 3;
Output:
merchant | distinct_agents | distinct_users | tx_count
-------------------+-----------------+----------------+----------
merch_jewelry | 4 | 4 | 6
merch_electronics | 3 | 3 | 3
merch_giftcard | 3 | 3 | 3
merch_grocery | 3 | 3 | 4
merch_travel | 3 | 3 | 3
Merchant cluster overlap is a noisier signal than the others because legitimate agents can naturally cluster on common merchants like merch_grocery. That is why the composite scoring weights it lightly and uses it as a tiebreaker rather than a primary indicator.
Composing Signals into a Collusion Score
The final view joins each transaction back to all five signal views and computes a per-agent score:
CREATE MATERIALIZED VIEW aap08_collusion_risk_mv AS
WITH agent_signals AS (
SELECT
t.agent_id,
t.user_id,
MAX(CASE WHEN sd.device_fingerprint IS NOT NULL THEN 1 ELSE 0 END) AS shared_device_flag,
MAX(CASE WHEN tc.merchant IS NOT NULL THEN 1 ELSE 0 END) AS time_burst_flag,
MAX(CASE WHEN ss.mandate_signer IS NOT NULL THEN 1 ELSE 0 END) AS shared_signer_flag,
MAX(CASE WHEN fo.funding_source IS NOT NULL THEN 1 ELSE 0 END) AS shared_funding_flag,
MAX(CASE WHEN mo.merchant IS NOT NULL THEN 1 ELSE 0 END) AS merchant_cluster_flag
FROM aap08_agent_tx t
LEFT JOIN aap08_shared_device_mv sd ON sd.device_fingerprint = t.device_fingerprint
LEFT JOIN aap08_time_correlated_mv tc
ON tc.merchant = t.merchant
AND t.tx_time >= tc.window_start
AND t.tx_time < tc.window_end
LEFT JOIN aap08_shared_signer_mv ss ON ss.mandate_signer = t.mandate_signer
LEFT JOIN aap08_funding_overlap_mv fo ON fo.funding_source = t.funding_source
LEFT JOIN aap08_merchant_overlap_mv mo ON mo.merchant = t.merchant
GROUP BY t.agent_id, t.user_id
)
SELECT
agent_id,
user_id,
shared_device_flag,
time_burst_flag,
shared_signer_flag,
shared_funding_flag,
merchant_cluster_flag,
(shared_device_flag * 25
+ time_burst_flag * 25
+ shared_signer_flag * 20
+ shared_funding_flag * 20
+ merchant_cluster_flag * 10) AS collusion_score,
CASE
WHEN (shared_device_flag * 25
+ time_burst_flag * 25
+ shared_signer_flag * 20
+ shared_funding_flag * 20
+ merchant_cluster_flag * 10) >= 70 THEN 'BLOCK'
WHEN (shared_device_flag * 25
+ time_burst_flag * 25
+ shared_signer_flag * 20
+ shared_funding_flag * 20
+ merchant_cluster_flag * 10) >= 40 THEN 'REVIEW'
ELSE 'ALLOW'
END AS action
FROM agent_signals;
The weights reflect signal strength: device sharing across users and time-correlated bursts each contribute 25 points, signer reuse and funding overlap each 20 points, and merchant cluster overlap 10. Two of the heavy signals plus one medium pushes an agent into BLOCK; one heavy plus one medium lands in REVIEW.
Querying the composite produces:
agent_id | user_id | dev | burst | sig | fund | merch | collusion_score | action
-----------+-----------+-----+-------+-----+------+-------+-----------------+--------
agent_a1 | user_u100 | 1 | 1 | 1 | 1 | 1 | 100 | BLOCK
agent_a2 | user_u101 | 1 | 1 | 1 | 1 | 1 | 100 | BLOCK
agent_a3 | user_u102 | 1 | 1 | 1 | 1 | 1 | 100 | BLOCK
agent_b1 | user_u200 | 0 | 1 | 0 | 1 | 1 | 55 | REVIEW
agent_b2 | user_u201 | 0 | 1 | 0 | 1 | 1 | 55 | REVIEW
agent_b3 | user_u202 | 0 | 1 | 0 | 0 | 1 | 35 | ALLOW
agent_b4 | user_u203 | 0 | 1 | 0 | 0 | 1 | 35 | ALLOW
agent_x4a | user_u503 | 1 | 0 | 0 | 0 | 1 | 35 | ALLOW
agent_d1 | user_u400 | 0 | 0 | 0 | 1 | 1 | 30 | ALLOW
agent_d2 | user_u401 | 0 | 0 | 0 | 1 | 1 | 30 | ALLOW
agent_d3 | user_u402 | 0 | 0 | 0 | 1 | 1 | 30 | ALLOW
agent_x4b | user_u503 | 1 | 0 | 0 | 0 | 0 | 25 | ALLOW
agent_c1 | user_u300 | 0 | 0 | 1 | 0 | 0 | 20 | ALLOW
agent_c2 | user_u301 | 0 | 0 | 1 | 0 | 0 | 20 | ALLOW
agent_x1 | user_u500 | 0 | 0 | 0 | 0 | 1 | 10 | ALLOW
agent_x7 | user_u506 | 0 | 0 | 0 | 0 | 1 | 10 | ALLOW
agent_x2 | user_u501 | 0 | 0 | 0 | 0 | 0 | 0 | ALLOW
agent_x3 | user_u502 | 0 | 0 | 0 | 0 | 0 | 0 | ALLOW
agent_x5 | user_u504 | 0 | 0 | 0 | 0 | 0 | 0 | ALLOW
agent_x6 | user_u505 | 0 | 0 | 0 | 0 | 0 | 0 | ALLOW
The cloned-agent ring (agent_a1, agent_a2, agent_a3) all hit every signal and score 100, classified BLOCK. The burst-convergence ring at the jewelry merchant lands in REVIEW for the two agents that also share funding (agent_b1, agent_b2) and the other two surface as a softer ALLOW with a non-trivial residual score that an analyst can drill into. The legitimate user_u503 who runs two of their own agents from one device receives a low score because their device is shared but their user_id is not, so the device signal correctly does not fire in isolation. The standalone D-cluster ring is tagged REVIEW-adjacent at 30 points: funding overlap and merchant cluster fire, but no device, time, or signer overlap pushes it higher; this is the kind of softer pattern an analyst should periodically sweep.
In production, a payment authorization service can query this view directly with a streaming query at sub-second latency, treating BLOCK as a hard reject, REVIEW as a hold-for-review queue, and ALLOW as proceed.
For deployment patterns and ingest options, see our companion piece on building real-time anomaly detection with streaming SQL, which covers Kafka source configuration, sink design, and exactly-once semantics for fraud-grade pipelines.
FAQ
What is multi-agent collusion in payment fraud?
Multi-agent collusion in payment fraud is the coordinated abuse of payment rails by two or more autonomous AI shopping or commerce agents that together create a fraudulent pattern that no single agent would trigger on its own. The agents may belong to the same human operator or to a ring of operators sharing infrastructure such as device fingerprints, payment instruments, or mandate signers, and they typically split a single attack across many low-velocity sessions to slip past per-agent rate limits and per-account spend caps. Standard per-agent rules cannot detect these patterns because every individual agent looks compliant.
What signals indicate AI agents are colluding?
The strongest cross-agent signals are shared device fingerprints across distinct user accounts, time-correlated transaction bursts at the same merchant within seconds of each other, mandate signers reused across multiple user identities, common funding sources funding ostensibly different agents, and clusters of agents converging on the same merchant or product SKU in a tight window. A single signal may be benign (a household sharing a laptop, two friends shopping at the same store), but two or more in combination is a strong indicator of coordinated abuse. A weighted composite score works much better than any single rule.
Can multiple agents from one user be legitimate?
Yes, and a good detection rule must accommodate this. A single user may legitimately operate a shopping agent, a travel agent, and a subscription manager agent, and these agents will share the same device, the same signer, and the same funding card. The detection rule must therefore distinguish a single user with multiple agents from multiple users sharing one device or signer. Counting distinct user_id values per device or signer (rather than distinct agents) is the simplest and most reliable way to draw that line. The example pipeline in this post shows a legitimate two-agent user landing safely in the ALLOW bucket.
How does RisingWave detect cross-agent collusion?
RisingWave maintains incremental materialized views over the live transaction stream. Each materialized view captures one collusion signal, such as shared device fingerprints across users, time-correlated bursts, or signer reuse, and a final composite view joins the signals into a per-agent collusion score. Because the views update incrementally as new transactions arrive, the score is always current and a payment authorization service can read it with millisecond latency. Production deployments source transactions from Kafka or a CDC stream, sink the score back to a low-latency store such as Redis or PostgreSQL, and enforce decisions inline at authorization time.
Conclusion
Agentic commerce will keep growing, and so will the surface area for coordinated agent fraud. The patterns shown here are not theoretical: cloned-agent rings, synthetic-identity collusion, and burst convergence are already emerging in agent-driven marketplaces and embedded checkout flows. Per-agent rules are necessary but insufficient. Cross-agent correlation is what closes the gap, and streaming SQL on top of incremental materialized views is the cleanest way to express that correlation in production.
The pipeline you just built is small but complete: thirty rows of data, six materialized views, one composite score, and the same shape scales to billions of transactions per day on RisingWave. Swap the table for a Kafka source, point the score at your authorization service, and you have a real-time multi-agent collusion detector behind your checkout.
Ready to detect multi-agent collusion in real time? Try RisingWave Cloud free and stand up the pipeline above in a few minutes.
Join our Slack community to discuss agentic commerce fraud patterns with other engineers building on streaming SQL.

