How Agent Attribution Works
Overview
This document explains how an AI agent earns commission on conversions it drives through the Syndicate Links attribution system. Two core problems make traditional affiliate tracking unworkable in agent contexts: (1) cookies are unavailable — agents operate outside browsers and have no cookie jar to read or write, and (2) there is no shared session between the agent and the merchant — the agent cannot inject tracking state into a user's browsing context. The system described here solves both problems with a two-layer, machine-to-machine credential model that requires no browser, no redirect, and no session.
Two-Layer Auth Model
Every conversion event must carry two independent authentication signals. Neither is sufficient on its own.
| Layer | Token Format | Purpose |
|---|---|---|
| Layer 1 | aff_agent_… Bearer token | Proves who is calling — identifies the registered affiliate |
| Layer 2 | slat_v1_… attribution token | Proves the event is legitimate — binds the call to a specific affiliate-program relationship and a unique nonce |
Layer 1 (aff_agent_ Bearer token) is a long-lived credential issued to a registered publisher. It authenticates the caller at the API boundary — confirming that the request comes from a known affiliate account. It does not by itself prove the event is bound to a valid program or that it hasn't been replayed.
Layer 2 (slat_v1_ attribution token) is a short-lived, HMAC-signed token minted at conversion time. It encodes the affiliate ID, program ID, tracking code, and a unique nonce. Its signature proves the token was created by a party with access to the affiliate's token secret, and the nonce prevents replay. It does not by itself prove the caller has valid API access.
Both layers must pass validation for a conversion event to be recorded.
Agent Key Flow
Agent keys are issued through the Syndicate Links affiliate publisher portal.
Registration
Register as a publisher at affiliate.syndicatelinks.co. After registration, your affiliateId is assigned and available in the portal dashboard.
Issuing an Agent Key
POST /affiliate/keys
Authorization: Bearer <your-session-token>
Content-Type: application/json
{
"type": "agent",
"label": "my-agent-v1"
}
Response:
{
"key": "aff_agent_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"type": "agent",
"label": "my-agent-v1",
"createdAt": "2026-04-03T00:00:00.000Z"
}
Key Handling
Agent keys are long-lived. They do not expire on a schedule, but can be revoked via the portal or API. Treat them as secrets:
- Store in a secret manager (e.g., AWS Secrets Manager, Vault, environment secrets in your deployment platform)
- Do not commit to source control
- Do not log the key value
- Rotate if compromised — issue a new key and revoke the old one
Attribution Token (slat_v1_) Format
Structure
slat_v1_{base64url(JSON payload)}.{HMAC-SHA256-hex}
The token is a dot-separated string. The prefix slat_v1_ is a literal string identifying the token type and version. The payload is standard base64url-encoded JSON (no padding). The signature is a lowercase hex-encoded HMAC-SHA256 of the full string slat_v1_{base64url(payload)} using the AGENT_TOKEN_SECRET.
Payload Fields
| Field | Type | Required | Description |
|---|---|---|---|
sub | string | Yes | Affiliate ID — must match the affiliateId of the authenticated aff_agent_ key |
prog | string | Yes | Program ID of the merchant program being attributed |
code | string | Yes | Tracking code for the program (assigned when you join) |
nonce | string (UUID v4) | Yes | Unique identifier for this event — used for replay protection |
iat | number | Yes | Token issuance time as Unix timestamp (seconds) |
exp | number | No | Optional expiry as Unix timestamp (seconds) — server rejects tokens past this time |
Example Payload
{
"sub": "aff_01HXYZ1234567890",
"prog": "prog_01HABC9876543210",
"code": "AGENTSPRING2026",
"nonce": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"iat": 1743638400
}
Signing
HMAC-SHA256(
key = AGENT_TOKEN_SECRET,
data = "slat_v1_" + base64url(JSON.stringify(payload))
)
The AGENT_TOKEN_SECRET is available in your affiliate dashboard under Settings → Agent Token Secret. It is scoped to your affiliate account.
Minting Timing
Tokens are minted at conversion time — not in advance. A token represents a specific conversion event. Do not cache or reuse tokens across events; each conversion requires a fresh nonce and a new token.
Example: Minting in Node.js
import crypto from 'crypto';
function mintAttributionToken({ affiliateId, programId, trackingCode, secret }) {
const payload = {
sub: affiliateId,
prog: programId,
code: trackingCode,
nonce: crypto.randomUUID(),
iat: Math.floor(Date.now() / 1000),
};
const encodedPayload = Buffer.from(JSON.stringify(payload))
.toString('base64url');
const data = `slat_v1_${encodedPayload}`;
const sig = crypto
.createHmac('sha256', secret)
.update(data)
.digest('hex');
return `${data}.${sig}`;
}
Replay Protection
Nonce Deduplication
Every slat_v1_ token includes a nonce — a UUID v4 generated fresh for each conversion event. The server stores nonces with a 48-hour TTL.
| Scenario | Server behavior |
|---|---|
| First time this nonce is seen | Event is recorded, 201 returned |
| Same nonce received again within 48h | 409 returned — event already recorded |
| Same nonce received after 48h | Nonce slot is freed; a new event would be recorded (avoid this — always use a fresh nonce) |
How to Handle 409
A 409 response on a conversion attempt means the server already has a record for this event. Treat 409 as success. Do not retry with the same token. Do not generate a new token for the same underlying conversion — that would create a duplicate commission claim.
The canonical pattern:
const res = await fetch('/v1/track/agent-attribution', { ... });
if (res.status === 201 || res.status === 409) {
// Event recorded (201) or already recorded (409) — both are success
return;
}
// Handle other status codes as errors
Auto-Approved Commissions
When POST /v1/track/agent-attribution returns 201, the commission is immediately recorded with commissionStatus: "approved". There is no manual review gate before approval.
201 Response Body
{
"eventId": "evt_01HXYZ9999999999",
"commissionAmount": 12.50,
"commissionStatus": "approved",
"programId": "prog_01HABC9876543210",
"recordedAt": "2026-04-03T00:00:00.000Z"
}
commissionAmount is in the currency configured by the merchant for the program. commissionStatus will always be "approved" in a 201 response — pending fraud scoring (see next section).
Fraud Scoring
Fraud scoring is asynchronous and non-blocking. The 201 is returned before fraud scoring completes. Scoring runs in the background after the event is recorded.
Signals
| Signal | Description |
|---|---|
| Order ID reuse | Same order ID claimed by more than one agent key |
| Commission velocity | Anomalous rate of successful attributions from a single affiliate |
| Token signature quality | Statistical patterns in nonce or payload fields inconsistent with normal minting behavior |
Post-Approval Flagging
High-confidence fraud signals can result in an event being flagged or reversed after the initial 201. When this happens, commissionStatus transitions from "approved" to "flagged" or "reversed". Webhooks are fired for status transitions if you have a webhook endpoint configured.
Fraud scoring does not block the 201. Your agent should not poll for fraud status before proceeding.
Full Request Flow
1. Agent joins a merchant program
↓
2. Agent retrieves the program's trackingCode
↓
3. At conversion time: agent mints a slat_v1_ token
↓
4. Agent sends POST /v1/track/agent-attribution
↓
5. Server validates (see below)
↓
6. Server returns 201 with commissionAmount
↓
7. Fraud scoring runs async (non-blocking)
Step-by-Step
Step 1 — Join a program.
Use the affiliate portal at affiliate.syndicatelinks.co or POST /affiliate/programs/{programId}/join via API. The program must be active and accepting affiliates.
Step 2 — Get the tracking code.
After joining, the program record includes a trackingCode scoped to your affiliate account for that program. Retrieve it from the portal or via GET /affiliate/programs/{programId}/membership.
Step 3 — Mint the token at conversion time.
Use your AGENT_TOKEN_SECRET to mint a slat_v1_ token. Include a fresh UUID v4 as nonce. Do not reuse tokens.
Step 4 — Send the attribution request.
POST /v1/track/agent-attribution
Authorization: Bearer aff_agent_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"attribution_token": "slat_v1_eyJzdWIiOiJhZmZfMDF...<payload>.3a9f1c2d...<sig>",
"order_id": "order_12345",
"amount": 99.00,
"currency": "USD"
}
order_id, amount, and currency are required fields alongside attribution_token.
Step 5 — Server validation. The server validates in this order:
- (a) Bearer token is a valid
aff_agent_key belonging to an active affiliate account - (b)
slat_v1_signature is valid (HMAC-SHA256 matches) - (c) Token
submatches theaffiliateIdof the authenticated Bearer key - (d) Nonce has not been seen in the past 48 hours
- (e) Program (
prog) is active and the affiliate is a member
If any check fails, the appropriate error code is returned (see Error Reference). No partial recording occurs.
Step 6 — 201 returned.
Commission is recorded immediately with commissionStatus: "approved".
Step 7 — Async fraud scoring. Fraud scoring runs after the response is sent. Does not affect the 201 or block the agent.
Error Reference
| Status | Meaning | Action |
|---|---|---|
400 | Missing required fields (attribution_token, order_id, amount, or currency absent or malformed) | Fix the request body |
401 | Invalid Bearer token, or slat_v1_ signature mismatch | Check aff_agent_ key and AGENT_TOKEN_SECRET; verify signing logic |
403 | Attribution token sub does not match the authenticated affiliate | Token was minted with a different affiliateId than the Bearer key belongs to |
404 | Program not found or inactive | Verify prog field; check program status in the portal |
409 | Nonce replay — event already recorded | Treat as success; do not retry |
429 | Rate limited | Back off and retry with exponential backoff |
500 | Server error | Retry with backoff; contact support if persistent |
Design Principles
- No cookies, no browser sessions, no redirects required. Attribution is recorded via a direct API call from the agent to the Syndicate Links server. No client-side tracking infrastructure is involved.
- Client-side token minting. The agent generates the
slat_v1_token locally using itsAGENT_TOKEN_SECRET. No pre-flight request to obtain a token is needed — one round-trip records the conversion. - Stateless from the agent's perspective. The agent holds two secrets (
aff_agent_key andAGENT_TOKEN_SECRET) and mints tokens on demand. There is no session to maintain, no token refresh, and no handshake sequence. - Nonce deduplication is fully server-side. The agent generates a UUID v4 and includes it in the token. The server is solely responsible for tracking seen nonces and enforcing the 48h TTL. The agent has no obligation to remember past nonces.
- Not OAuth. This is machine-to-machine credential binding. There is no user delegation, no authorization code flow, no token exchange, and no scopes. The
aff_agent_key represents the affiliate, not an end user.
Related Resources
- What Is Agent Attribution? — /docs/what-is-agent-attribution — the explainer: what agent attribution is, why cookies fail, FAQ
- x402 & Agent Attribution — /docs/x402-attribution —
atxp_referencestandard and carrying attribution through x402 payment flows - ACP Attribution Reference — /docs/acp-attribution
- Getting Started — /docs/getting-started
- API Reference — /docs/api-reference
- MCP Server —
npx syndicate-links-mcp— 7 attribution and affiliate tools for Claude Desktop or any MCP-compatible agent runtime