Developer Guide: Build, Price, and Bill with Paid

At-a-Glance

StepInterfaceWhat you’ll doOutcome
1: Sign upWeb AppGo to app.paid.ai, and register with your work email. During sign-up you’ll set the organization nameA dedicated, secure workspace for your company—isolated from other tenants and ready for configuration.
2: Create an API keyWeb AppGenerate and copy your secret token.Credentials to call the Paid API.
3: Create a ProductWeb AppConfigure pricing in Products > Create.A product_id used in every request.
4: Integrate Accounts / Contacts/ OrdersAPIFor every new or updated record in your platform, immediately POST a JSON payload to:
/customers (for companies or buying groups)
/contacts (for billing contact or other people working at the account)
/orders (for subscriptions)

This near-real-time push keeps Paid perfectly mirrored—no batching or nightly jobs needed.
A continuously live, up-to-the-second copy of your account, contact, and order data inside Paid.
5: Stream SignalsAPIFor every runtime event, send a Signal payload to PaidPaid ingests each Signal in real time, applies your pricing and costing, and shows them against your customer and order.

Step 1: Sign up

Go to app.paid.ai, and register with your work email. During sign-up you’ll set the organization name and details.

Step 2: Creating an API Key

API keys are your credentials to authenticate with the Paid API.

  1. Sign in to your Paid dashboard at app.paid.ai
  2. Navigate to your organization settings
  3. Go to the API Keys section
  4. Click “Create New API Key”
  5. Give your key a descriptive name
  6. Copy the generated secret key immediately - you won’t be able to see it again!

Security Best Practices

  • Never commit API keys to your repository
  • Store keys in environment variables or a secure secret manager
  • Rotate keys regularly
  • Use different keys for different environments (development, staging, production)

Step 3: Product Creation

Create Your First Product

For the purpose of this example, we are going to use an AI SDR product as a reference.

  1. Navigate to Products in the sidebar
  2. Click Create
  3. Fill in the product details:
    • Product name (required): A recognizable name (e.g., “AI SDR”). This name will appear on invoices.
    • Description: What this Product does (e.g., “Automates SDR activities”).
    • External ID: Your internal identifier for API integration (e.g., ai-sdr-001).
    • Product code: Identifier for accounting purposes (e.g., PROD-001).

Tip: If you have multiple AI agents (sales chatbot, service chatbot, etc.), create a separate Product for each so they can be tracked and billed separately.

Configure Pricing

After entering product details, add pricing using the Add pricing dropdown. You can combine multiple pricing types:

  • One-time fee

    A single charge when a customer signs up or onboards.

  • Platform fee

    A recurring flat fee (monthly/annual) for ongoing platform access.

  • Per seat

    A recurring charge per user or seat.

  • Usage pricing

    Variable charges based on activity Signals—the event name you specify here (e.g., email_sent) will be matched against Signals you send via API.

  • Outcome pricing

    Variable charges based on outcome Signals—successful results (e.g., meeting_booked, lead_qualified).

Example Configuration

For an AI SDR agent, you might add:

  1. Platform fee: $500/month base access
  2. Usage pricing: $0.10 per email_sent signal
  3. Outcome pricing: $50 per meeting_booked signal

Click Finish to create the product, or Save as draft to continue later.

Copy the Product ID from the URL or product details page. You’ll need this for API integration.

Step 4: Integrate Accounts / Contacts / Orders

Your platform already lets new customers sign up, choose a plan, and start using your products. Paid needs to see the same objects—Accounts, Contacts, and Orders—so it can price usage, generate invoices, and collect payments.

This section walks through a complete, end-to-end example. Copy, paste, and adapt these calls to wire Paid into your onboarding flow.

Scenario

John Doe, VP of Sales at Acme Ltd, signs up for your AI SDR – Pro plan on 1 June 2025.

In your database you will create:

  1. AccountAcme Ltd
  2. ContactJohn Doe
  3. Order → their paid subscription to AI SDR (Pro)

We will mirror those three objects inside Paid with the API.

Prerequisites

1pip install paid-python

1: Create the Account

1from paid import Paid, TaxExemptStatus, CreationSource, Address
2
3# Initialize the client
4client = Paid(token="<YOUR_API_KEY>")
5
6# Create the account
7customer = client.customers.create(
8 name="Acme Ltd",
9 phone="+1-555-0123",
10 employee_count=100,
11 annual_revenue=1000000,
12 tax_exempt_status="none",
13 creation_source="api",
14 website="https://acme.com",
15 external_id="acme_account_123", # this would normally be this account's ID in your DB
16 billing_address=Address(
17 line_1="123 Business Ave",
18 line_2="Suite 100",
19 city="San Francisco",
20 state="CA",
21 zip_code="94105",
22 country="USA"
23 )
24)

Returns

Returns the Customer object upon successful creation. Save the returned id—we’ll need it in the next two calls.

1{
2 id: 'uuid',
3 organizationId: 'uuid',
4 name: 'Acme Ltd',
5 phone: '+1-555-0123',
6 website: 'https://acme.com',
7 employeeCount: 100,
8 annualRevenue: 1000000,
9 taxExemptStatus: 'none',
10 creationSource: 'api',
11 externalId: 'acme_123',
12 creationState: 'active',
13 billingAddress: {
14 city: 'San Francisco',
15 line1: '123 Business Ave',
16 line2: 'Suite 100',
17 state: 'CA',
18 country: 'USA',
19 zipCode: '94105'
20 }
21}

2: Create the Contact

1from paid import Paid, Salutation
2
3# Initialize the client
4client = Paid(token="<YOUR_API_KEY>")
5
6# Create the contact (using either an internal or an external account id)
7contact = client.contacts.create(
8 customer_external_id="acme_account_123", # use either customer_id or customer_external_id for contact creation
9 salutation="Mr.",
10 first_name="John",
11 last_name="Doe",
12 email="john.doe@acme.com",
13 phone="+1-555-0123",
14 billing_street="123 Main St",
15 billing_city="San Francisco",
16 billing_state_province="CA",
17 billing_country="USA",
18 billing_postal_code="94105",
19 external_id="acme_contact_123"
20)

Returns

Returns the Contact object upon successful creation. It’s recommended to store the returned contact ID in your own system for future reference and operations. The contact is now linked to the account inside Paid.

1{
2 id: 'uuid',
3 organizationId: 'uuid',
4 salutation: 'Mr.',
5 firstName: 'John',
6 lastName: 'Doe',
7 accountName: 'Acme Ltd',
8 email: 'john.doe@acme.com',
9 phone: '+1-555-0123',
10 billingStreet: '123 Main St',
11 billingCity: 'San Francisco',
12 billingStateProvince: 'CA',
13 billingCountry: 'USA',
14 billingZipPostalCode: '94105',
15 externalId: 'acme_contact_123',
16 accountId: 'uuid'
17}

3: Create an Order

1from paid import Paid
2
3# Initialize the client
4client = Paid(token="<YOUR_API_KEY>")
5
6# Create the order (using either an internal or an external customer id and billing contact id)
7order = client.orders.create(
8 customer_id=customer.id, # use either customer_id or customer_external_id for order creation
9 billing_contact_id=contact.id, # use the contact ID from the previous step
10 name="AI SDR – Pro plan",
11 description="Annual subscription for AI SDR – Pro plan",
12 start_date="2025-06-01",
13 end_date="2026-05-31",
14 currency="USD"
15)

Returns

Returns the Order object upon successful creation. It’s recommended to store the returned order ID in your own system for future reference and operations.

1{
2 "id": "uuid",
3 "organizationId": "uuid",
4 "name": "Annual subscription for AI SDR – Pro plan",
5 "startDate": "2024-01-01T00:00:00.000Z",
6 "endDate": "2024-12-31T00:00:00.000Z",
7 "orderAmount": 0,
8 "estimatedTax": 0,
9 "billedAmountNoTax": 0,
10 "billedTax": 0,
11 "pendingBillingAmount": 0,
12 "totalBilledAmount": 0,
13 "totalAmount": 0,
14 "creationState": "draft",
15 "usageSummary": [],
16 "customer": {
17 "id": "uuid",
18 "organizationId": "uuid",
19 "name": "Acme Ltd",
20 "email": "contact@acme.com",
21 "phone": "",
22 "website": "https://acme.com",
23 "employeeCount": 100,
24 "annualRevenue": 1000000,
25 "taxExemptStatus": "exempt",
26 "creationSource": "api",
27 "externalId": "customer-123",
28 "creationState": "active",
29 "billingAddress": {
30 "city": "New York",
31 "line1": "123 Main St",
32 "line2": "Suite 100",
33 "state": "NY",
34 "country": "USA",
35 "zipCode": "10001"
36 }
37 },
38 "customerId": "uuid",
39 "orderLines": [...]
40}

Step 5: Stream Signals (AI SDR Edition)

Paid measures how each Product is used through Signals—immutable events you emit in real time. Every Signal links back to the Order you created (for example, Acme Ltd’s Annual subscription for AI SDR – Pro plan), so Paid can measure all your product activities against it, and can also price the event and invoice it if you charge your customers on outcome.

We’ll use an AI SDR example and stream Signals for common SDR activities: sending outbound emails, making calls, logging replies, and booking meetings.

When should I send a Signal?

ActivitySend?Signal Name
AI SDR sends a prospecting emailYesemail_sent
Prospect replies (AI ingests reply)Yesemail_reply
AI SDR places an outbound callYescall_completed
Call goes to voicemail (no conversation)No
Meeting booked & pushed to CRMYesmeeting_booked
Internal nightly refresh jobNo

Only instrument activities and outcomes that mean something to your customers.

1from paid import Paid, SignalV2
2
3# Initialize the client
4client = Paid(token="<YOUR_API_KEY>")
5
6product_id = "product_123"
7customer_id = "customer_123"
8signal_name = "email_sent"
9additional_data = {
10 "model": "gpt4",
11 "subjectLength": 56,
12 "bodyLength": 452
13}
14
15
16# Create signals for bulk usage recording
17signal = SignalV2(
18 event_name=signal_name,
19 product_id=product_id,
20 customer_id=customer_id,
21 data=additional_data
22)
23
24# Record bulk usage
25result = client.usage.record_bulk_v2(signals=[signal])

Sending Signals via OTEL Traces

A more reliable and user-friendly way to send Signals is to send them via OpenTelemetry tracing. This allows you to send Signals with less arguments and boilerplate as the information is available in the tracing context. The arguments take in signal name, optional data, and a flag that attaches costs from the same trace.

Paid.signal() has to be called within a tracing context. Tracing context is created with Paid.trace().

1from paid import Paid
2
3# Initialize the client
4client = Paid(token="<YOUR_API_KEY>")
5
6# Initialize tracing
7client.initialize_tracing()
8
9def send_email():
10 # Emit signal within a trace
11 client.signal(
12 event_name="email_sent",
13 data={ } # Optional data (ex. Manual Cost Tracking)
14 )
15 # Simulate the rest of your logic
16 print("Email sent!")
17
18# Wrap it in a trace
19_ = client.trace(
20 external_customer_id="customer_123",
21 external_product_id="product_123", # external_product_id is generally optional but is required for sending signals.
22 fn=lambda: send_email()
23)

Error Handling

The API uses conventional HTTP response codes to indicate the success or failure of requests:

  • 2xx range indicates success
  • 4xx range indicates an error that failed given the information provided
  • 5xx range indicates an error with Paid’s servers

Error Response Format

1{
2 "error": {
3 "message": "Error message",
4 "code": "ERROR_CODE",
5 "details": "Additional error details"
6 }
7}

Need Help?

Additional Resources