Integrating Signals with Cost Tracking

Overview

This guide shows you how to integrate cost tracking with signals in your AI agent workflow. By linking these two together, you can:

  • Automatically attribute AI provider costs (OpenAI, Anthropic, etc.) to specific business events
  • Track the true cost of achieving business outcomes (e.g., booking a meeting, sending an email, processing an image or a document)
  • Generate accurate invoices that reflect both usage-based pricing and actual AI costs

Prerequisites

Before you begin, make sure you have:

  • Created an API key in the Paid dashboard
  • Installed the Paid SDK for your language
  • Configured at least one Agent with signals
  • Set up your customer and order records

Note: This feature is currently available in Python and Node.js SDKs only. Support for Go, Ruby, and Java is coming soon.

How It Works

In a typical AI agent workflow, your event processing logic might look like this:

1. Receive event (e.g., "prospect replied to email")
2. Process event → make AI calls to generate response
3. Send response
4. Send signal to Paid

To link AI costs with signals, you need to:

  1. Wrap your AI provider calls with Paid wrappers or hooks (e.g., PaidOpenAI, PaidAnthropic)
  2. Wrap your entire event processing function in a tracing context (Paid.trace() or @paid_tracing())
  3. Emit the signal within the same trace with enable_cost_tracing=True

This ensures all AI calls and the signal share the same OpenTelemetry trace, allowing Paid to link costs to your business outcome.

Important: Cost Tracing Rules

Only enable cost tracing for ONE signal per trace

If you emit multiple signals within a single trace and enable cost tracing for more than one, the costs will be double-counted across multiple signals. The pattern should be:

  • Multiple AI calls + ONE signal with cost tracing = Costs attributed to that signal
  • Multiple signals = Only one should have enable_cost_tracing=True

Basic Integration Pattern

Here’s the recommended pattern for integrating cost tracking with signals:

1from paid import Paid
2from paid.tracing import paid_tracing
3from paid.tracing.wrappers import PaidOpenAI
4from openai import OpenAI
5
6# Initialize Paid client
7client = Paid(token="<YOUR_API_KEY>")
8
9# Wrap your AI provider client
10openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
11
12@paid_tracing("customer_123", "agent_123")
13def process_event(event):
14 """
15 Process an agent event - wrapped in tracing context
16 """
17 # Step 1: Make AI calls (automatically traced)
18 response = openai_client.chat.completions.create(
19 model="gpt-4",
20 messages=[
21 {"role": "system", "content": "You are an AI SDR assistant"},
22 {"role": "user", "content": event.content}
23 ]
24 )
25
26 # Step 2: Your business logic
27 send_response(response.choices[0].message.content)
28
29 # Step 3: Emit signal with cost tracing enabled
30 client.signal(
31 event_name="email_sent",
32 data={"response_length": len(response.choices[0].message.content)},
33 enable_cost_tracing=True # Link costs to this signal
34 )
35
36# Call your event processor
37process_event(incoming_event)

Real-World Example: AI SDR Agent

Let’s walk through a complete example of an AI SDR agent that processes email replies and attributes costs to the appropriate signal.

1from paid import Paid
2from paid.tracing import paid_tracing
3from paid.tracing.wrappers import PaidOpenAI
4from openai import OpenAI
5
6# Initialize
7client = Paid(token="<YOUR_API_KEY>")
8openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
9
10@paid_tracing("acme_account_123", "ai_sdr_agent")
11def handle_email_reply(email):
12 """
13 Handle incoming email reply from prospect
14 """
15 # AI call #1: Analyze sentiment
16 sentiment_response = openai_client.chat.completions.create(
17 model="gpt-4",
18 messages=[
19 {"role": "system", "content": "Analyze email sentiment"},
20 {"role": "user", "content": email.body}
21 ]
22 )
23
24 sentiment = sentiment_response.choices[0].message.content
25
26 # AI call #2: Generate response
27 reply_response = openai_client.chat.completions.create(
28 model="gpt-4",
29 messages=[
30 {"role": "system", "content": f"Generate reply. Sentiment: {sentiment}"},
31 {"role": "user", "content": email.body}
32 ]
33 )
34
35 reply_text = reply_response.choices[0].message.content
36
37 # Business logic: Send the email
38 send_email(
39 to=email.sender,
40 subject=f"Re: {email.subject}",
41 body=reply_text
42 )
43
44 # Emit signal - costs from BOTH AI calls will be attributed here
45 client.signal(
46 event_name="email_reply_processed",
47 data={
48 "sentiment": sentiment,
49 "reply_length": len(reply_text)
50 },
51 enable_cost_tracing=True
52 )
53
54 # Optional: Emit additional signal WITHOUT cost tracing
55 # (to track the event without duplicating costs)
56 client.signal(
57 event_name="email_sentiment_analyzed",
58 data={"sentiment": sentiment},
59 enable_cost_tracing=False # Don't link costs to this one
60 )
61
62# Process incoming email
63handle_email_reply(incoming_email)

Event Loop Integration

Most AI agents run in an event loop, processing events continuously. Here’s how to integrate cost tracking in that pattern:

1from paid import Paid
2from paid.tracing import paid_tracing
3from paid.tracing.wrappers import PaidOpenAI
4from openai import OpenAI
5
6# Initialize once
7client = Paid(token="<YOUR_API_KEY>")
8openai_client = PaidOpenAI(OpenAI(api_key="<OPENAI_API_KEY>"))
9
10@paid_tracing("customer_123", "agent_123")
11def process_agent_event(event):
12 """Process a single agent event with cost tracking"""
13
14 # Make AI calls as needed
15 if event.type == "email_received":
16 response = openai_client.chat.completions.create(
17 model="gpt-4",
18 messages=[{"role": "user", "content": event.content}]
19 )
20 handle_email_response(response)
21
22 # Emit signal with cost tracing
23 client.signal(
24 event_name="email_processed",
25 enable_cost_tracing=True
26 )
27
28 elif event.type == "call_completed":
29 # Different AI calls for call processing
30 summary = openai_client.chat.completions.create(
31 model="gpt-4",
32 messages=[{"role": "user", "content": f"Summarize: {event.transcript}"}]
33 )
34
35 client.signal(
36 event_name="call_summarized",
37 enable_cost_tracing=True
38 )
39
40# Event loop
41while True:
42 event = get_next_event() # Your event queue
43 if event:
44 process_agent_event(event) # Each call creates a new trace

Supported AI Provider Wrappers and Hooks

The following AI provider wrappers and hooks are available for cost tracking:

Python SDK

  • PaidOpenAI / PaidAsyncOpenAI - OpenAI API
  • PaidOpenAIAgentsHook - Openai Agents hook
  • PaidAnthropic / PaidAsyncAnthropic - Anthropic Claude API
  • PaidMistral - Mistral AI API
  • PaidBedrock - AWS Bedrock
  • PaidGemini - Google Gemini
  • PaidLlamaIndexOpenAI - LlamaIndex with OpenAI
  • PaidLangChainCallback - LangChain hook

Node.js SDK

  • PaidOpenAI - OpenAI API (import from @paid-ai/paid-node/openai)
  • PaidAnthropic - Anthropic Claude API (import from @paid-ai/paid-node/anthropic)
  • PaidMistral - Mistral AI API (import from @paid-ai/paid-node/mistral)
  • PaidLangChainCallback - LangChain callback handler (import from @paid-ai/paid-node/langchain)
  • Vercel AI SDK wrapper (import from @paid-ai/paid-node/vercel)

Best Practices

1. One Signal, One Outcome

Each trace should represent a single logical business outcome with one cost-enabled signal:

1# ✅ Good: One trace = One outcome
2@paid_tracing("customer_123", "agent_123")
3def book_meeting(prospect):
4 # Multiple AI calls OK
5 analyze_calendar(prospect)
6 generate_invite(prospect)
7 send_confirmation(prospect)
8
9 # Single signal with cost tracing
10 client.signal("meeting_booked", enable_cost_tracing=True)
1# ❌ Bad: Multiple cost-enabled signals in one trace
2@paid_tracing("customer_123", "agent_123")
3def process_workflow(prospect):
4 analyze_calendar(prospect)
5 client.signal("calendar_analyzed", enable_cost_tracing=True) # Costs counted
6
7 generate_invite(prospect)
8 client.signal("invite_generated", enable_cost_tracing=True) # Costs counted AGAIN!

2. Wrap the Entire Event Handler

Make sure your tracing context includes all AI calls and the signal:

1# ✅ Good: Everything in one trace
2@paid_tracing("customer_123", "agent_123")
3def process_event(event):
4 ai_call_1()
5 ai_call_2()
6 client.signal("event_processed", enable_cost_tracing=True)
7
8# ❌ Bad: AI calls outside trace
9def process_event(event):
10 ai_call_1() # Cost not tracked!
11
12 @paid_tracing("customer_123", "agent_123")
13 def emit_signal():
14 client.signal("event_processed", enable_cost_tracing=True)
15
16 emit_signal()

Troubleshooting

Costs Not Appearing in Dashboard

If costs aren’t showing up linked to your signals:

  1. Check tracing is initialized: Call client.initialize_tracing() or use @paid_tracing decorator
  2. Verify wrapper usage: Make sure you’re using PaidOpenAI not regular OpenAI
  3. Confirm signal is in trace: The signal must be emitted within the traced function
  4. Check enable_cost_tracing flag: Make sure it’s set to True for the signal
  5. Verify external_agent_id: This is required when emitting signals

Double-Counted Costs

If costs appear multiple times:

  • Check that only ONE signal per trace has enable_cost_tracing=True
  • Make sure you’re not accidentally calling the traced function multiple times

Missing AI Provider Costs

If some AI calls aren’t being tracked:

  • Ensure all AI client calls are using the Paid wrapper or a hook
  • Check that the wrapper supports your AI provider (see supported wrappers above)
  • Verify the wrapper is created before entering the trace

Next Steps

Need Help?

Additional Resources