Overview
Customer and order attribution links your AI traces to the customers and transactions that triggered them. With it you can:
Measure conversions : which conversations ultimately led to a payment
Per-customer cost accounting : how much LLM spend each customer consumes
Fast troubleshooting : customer complaint → find the corresponding trace → reproduce the issue
Optimize profit : revenue (orders) minus cost (traces) = per-customer profit
Property Specification
Use the following standard property names — the Anyway toolchain automatically recognizes and aggregates customer data.
SDK Enum Property Key When to Set Example Value USER_IDuser_idEvery request from an identified user user-123CUSTOMER_IDcustomer_idEvery request from an identified customer CUS_a1b2c3d4e5f6ORDER_IDorder_idIn payment-related flows ORD_x7y8z9SESSION_IDsession_idSession tracking SES_a1b2c3d4e5f6
All properties are set via association properties — the SDK automatically propagates them to every span under the workflow.
Usage Recommendations
USER_ID : Set at the request entry point so it applies to the entire trace — use this for your application’s internal user identifier
CUSTOMER_ID : Set when you have an Anyway-format customer ID (e.g., from the Customers API)
ORDER_ID : Set in payment-related workflows (e.g., creating a Payment Link, processing a payment)
Use the AssociationProperty enum provided by the SDK to avoid hand-writing strings
Customer IDs must use the Anyway format: CUS_<12chars>. Obtain them via the Customers API (GET /v1/customers).
JavaScript SDK
Per-Request Setup (Recommended)
The most common pattern: set the user ID in request-handling middleware so that every trace for that user is automatically attributed. If you also have an Anyway customer ID, include it alongside.
import { withWorkflow , AssociationProperty } from "@anyway-sh/node-server-sdk" ;
app . post ( "/chat" , async ( req , res ) => {
const userId = req . auth . userId ; // From your auth middleware
const customerId = req . auth . customerId ; // Optional: Anyway customer ID
const result = await withWorkflow (
{
name: "chat" ,
associationProperties: {
[AssociationProperty. USER_ID ]: userId ,
[AssociationProperty. CUSTOMER_ID ]: customerId ,
},
},
async () => {
// All LLM spans inside automatically inherit these properties
return await agent . chat ( req . body . message );
},
);
res . json ( result );
});
Class Decorator Approach
import { workflow , task , AssociationProperty } from "@anyway-sh/node-server-sdk" ;
class ChatService {
private customerId : string ;
constructor ( customerId : string ) {
this . customerId = customerId ;
}
@ workflow (( self ) => ({
name: "chat" ,
associationProperties: {
[AssociationProperty. CUSTOMER_ID ]: ( self as ChatService ). customerId ,
},
}))
async chat ( message : string ) {
return await this . classify ( message );
}
@ task ({ name: "classify" })
async classify ( message : string ) {
// LLM call — automatically inherits customer attribution properties
}
}
Setting Order ID / Product ID in Payment Flows
In payment-related workflows, pass order and product information via association properties:
import { withWorkflow , AssociationProperty } from "@anyway-sh/node-server-sdk" ;
async function handlePayment ( customerId : string , productId : string ) {
return withWorkflow (
{
name: "process_payment" ,
associationProperties: {
[AssociationProperty. CUSTOMER_ID ]: customerId ,
},
},
async () => {
const link = await paymentApi . createLink ( productId );
// Once we have the orderId, set it in the inner workflow/task
return withWorkflow (
{
name: "complete_order" ,
associationProperties: {
[AssociationProperty. CUSTOMER_ID ]: customerId ,
[AssociationProperty. ORDER_ID ]: link . orderId ,
},
},
async () => {
return await fulfillOrder ( link . orderId );
},
);
},
);
}
Python SDK
Per-Request Setup (Recommended)
from anyway.sdk import Traceloop
from anyway.sdk.decorators import workflow, task
from anyway.sdk.associations import AssociationProperty
def handle_chat ( user_id : str , customer_id : str , message : str ):
Traceloop.set_association_properties({
AssociationProperty. USER_ID : user_id,
AssociationProperty. CUSTOMER_ID : customer_id,
})
return chat_workflow(message)
@workflow ( name = "chat" )
def chat_workflow ( message : str ):
return agent.chat(message)
Dynamically Setting Customer ID (From Request Context)
from anyway.sdk import Traceloop
from anyway.sdk.decorators import workflow, task
from anyway.sdk.associations import AssociationProperty
def chat_handler ( request ):
user_id = request.auth[ "user_id" ]
customer_id = request.auth[ "customer_id" ]
# Set for all spans in the current context
Traceloop.set_association_properties({
AssociationProperty. USER_ID : user_id,
AssociationProperty. CUSTOMER_ID : customer_id,
})
return process_message(request.body[ "message" ])
@workflow ( name = "process_message" )
def process_message ( message : str ):
return call_llm(message)
Setting Order ID / Product ID in Payment Flows
from anyway.sdk import Traceloop
from anyway.sdk.decorators import workflow, task
from anyway.sdk.associations import AssociationProperty
def process_payment ( customer_id : str , product_id : str ):
Traceloop.set_association_properties({
AssociationProperty. CUSTOMER_ID : customer_id,
})
link = payment_api.create_link(product_id)
return complete_order(customer_id, link.order_id)
def complete_order ( customer_id : str , order_id : str ):
Traceloop.set_association_properties({
AssociationProperty. CUSTOMER_ID : customer_id,
AssociationProperty. ORDER_ID : order_id,
})
return fulfill_order_workflow(order_id)
@workflow ( name = "fulfill_order" )
def fulfill_order_workflow ( order_id : str ):
return fulfill_order(order_id)
Common Scenarios
Anonymous User → Registered User
When a user transitions from anonymous to registered/paying:
// Phase 1: Anonymous session
await withWorkflow (
{
name: "chat" ,
associationProperties: {
[AssociationProperty. CUSTOMER_ID ]: `anon_ ${ sessionId } ` , // Temporary ID
},
},
async () => {
// Interactions before registration
},
);
// Phase 2: After registration/payment — use the real customer ID
await withWorkflow (
{
name: "chat" ,
associationProperties: {
[AssociationProperty. CUSTOMER_ID ]: "CUS_a1b2c3d4e5f6" , // Real ID from order system
},
},
async () => {
// Interactions after registration
},
);
To link anonymous and identified traces, store an anon_<sessionId> → CUS_xxx mapping in your application layer. Query both IDs when analyzing the full customer journey.
Next Steps
Cost Tracking Configure model pricing for accurate cost calculations
Tracing Learn about Workflows, Tasks, and Span Attributes