Reconcile
Custom Attributes

Custom Attributes

Enrich your data with flexible custom metadata for reporting and integration


Custom attributes provide a flexible way to enrich your Payload objects with business-specific metadata. Every object in the Payload API supports custom attributes via the attrs property. Add custom data to any object including transactions, invoices, accounts, billing schedules, and more. These attributes appear in the dashboard, can be used in reports, are searchable, and serve as reference information for system integrations.

Common Use Cases

  • Business Context - Add order IDs, customer references, project codes, and purchase order numbers to link transactions to your business systems
  • Reporting & Analytics - Track by department, cost center, product category, or region for detailed financial reporting and segmentation
  • Customer Segmentation - Store customer tier, industry, account health scores, and lifetime value for targeted analysis
  • System Integration - Connect with external systems using CRM IDs, sync timestamps, and reconciliation status (use underscore prefix to hide from dashboard)

Prerequisites

Before using custom attributes, understand:


What Are Custom Attributes


Custom attributes extend Payload objects with your business-specific data.

The attrs property

Every Payload object includes an attrs property that accepts any JSON structure:

{
  "id": "txn_abc123",
  "type": "payment",
  "amount": 150.00,
  "attrs": {
    "Order ID": "ORD-2024-5678",
    "Project Code": "PROJ-Q1",
    "department": "Sales",
    "_crm_account_id": "sf_00123456"
  }
}

Key characteristics:

  • Flexible structure: Add any fields you need
  • No predefined schema: Fields are not enforced
  • Nested objects supported: Create complex structures
  • Queryable: Filter and search by attribute values
  • Dashboard visible: Appears in Payload dashboard (except underscore-prefixed fields)
  • Report friendly: Available in dashboard reports
  • Integration fields: Prefix with underscore (_) to hide from dashboard

Where to use custom attributes

Custom attributes are available on every object in the Payload API, including:

  • Transactions - Payments, payouts, transfers
  • Invoices - Invoice-level metadata and tracking
  • Accounts - Customer segmentation and classification
  • Billing Schedules - Subscription and contract information
  • Payment Methods - Card or bank account metadata
  • Billing Items - Line item categorization
  • And all other API objects - Every object supports custom attributes

Adding Custom Attributes


Add custom attributes when creating or updating objects.

import payload
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Add custom attributes to a transaction for enrichment
 
transaction = pl.Transaction.create(
    type='payment',
    amount=150.00,
    sender={
        'account_id': 'acct_customer123'
    },
    receiver={
        'account_id': 'acct_merchant456'
    },
    attrs={
        # Business context
        'order_id': 'ORD-2024-5678',
        'customer_reference': 'CUST-ABC-001',
        'sales_rep': '[email protected]',
 
        # Categorization
        'department': 'Sales',
        'cost_center': 'CC-SALES-001',
        'project_code': 'PROJ-2024-Q1',
 
        # Compliance tracking
        'approval_code': 'APR-12345',
        'approved_by': '[email protected]',
        'approval_date': '2024-01-15',
 
        # Integration metadata
        'external_transaction_id': 'EXT-TXN-999',
        'source_system': 'ERP',
        'sync_status': 'completed'
    }
)
 
print(f'Transaction created: {transaction.id}')
print(f'Custom attributes: {transaction.attrs}')
print('These attributes will appear in dashboard and reports')

Enrich transactions with business context, categorization, and tracking information.

Receipt formatting:

Transaction attributes appear on customer receipts. Use display-friendly key names with title case and spaces (e.g., "Order ID", "Project Code") for customer-facing fields. Use snake_case for internal fields and underscore prefix for integration-only fields.

import payload
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Add custom attributes to an invoice for tracking
 
invoice = pl.Invoice.create(
    due_date='2024-02-15',
    description='Professional Services - January 2024',
    payer={
        'account_id': 'acct_customer123'
    },
    biller={
        'account_id': 'acct_merchant456'
    },
    items=[
        {
            'type': 'line_item',
            'description': 'Consulting Services',
            'line_item': {
                'value': 5000.00,
                'qty': 1
            }
        }
    ],
    attrs={
        # Contract information
        'contract_id': 'CNT-2024-001',
        'contract_start': '2024-01-01',
        'contract_end': '2024-12-31',
 
        # Customer context
        'customer_tier': 'enterprise',
        'account_manager': '[email protected]',
        'region': 'North America',
 
        # Billing details
        'billing_period': '2024-01',
        'service_month': 'January',
        'hours_billed': 100,
        'rate_per_hour': 50.00,
 
        # Payment terms
        'net_terms': 30,
        'late_fee_applicable': True,
        'discount_applied': False
    }
)
 
print(f'Invoice created: {invoice.id}')
print('Custom attributes enriched for reporting')

Add invoice-level metadata for contract tracking and reporting.

import payload
 
pl = payload.Session("secret_key_3bW9...", api_version="v2")
account = pl.Account.get("acct_customer123")
 
# Add custom attributes to an account for segmentation
 
 
account.update(
    attrs={
        # Customer information
        "customer_id": "CRM-CUST-5678",
        "company_name": "Acme Corporation",
        "industry": "Technology",
        "company_size": "500-1000",
        # Integration tracking
        "crm_sync_date": "2024-01-15T10:30:00Z",
        "erp_customer_id": "ERP-5678",
        "external_system_ids": {
            "salesforce": "SF-001-ABC",
            "netsuite": "NS-CUST-999",
        },
    }
)
 
print(f"Account updated: {account.id}")
print("Custom attributes available for segmentation and reporting")

Segment and classify accounts for reporting and analysis.

Querying Custom Attributes


Filter and search objects using custom attribute values.

import payload
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Query objects using custom attributes
 
# Find transactions by custom attribute
sales_transactions = pl.Transaction.filter_by(
    q='attrs[department] == "Sales"'
).all()
 
print(f'Found {len(sales_transactions)} sales transactions')
 
# Find transactions for specific project
project_transactions = pl.Transaction.filter_by(
    q='attrs[project_code] == "PROJ-2024-Q1"'
).all()
 
print(f'Found {len(project_transactions)} transactions for project')
 
# Find invoices by customer tier
enterprise_invoices = pl.Invoice.filter_by(
    q='attrs[customer_tier] == "enterprise"'
).all()
 
print(f'Found {len(enterprise_invoices)} enterprise invoices')
 
# Find accounts by region
west_coast_accounts = pl.Account.filter_by(
    q='attrs[sales_region] == "US-West"'
).all()
 
print(f'Found {len(west_coast_accounts)} West Coast accounts')
 
# Complex query with multiple attributes
high_value_sales_transactions = pl.Transaction.filter_by(
    q='attrs[department] == "Sales" && attrs[approval_code] != null && amount >= 1000.00',
    order_by='desc(amount)'
).all()
 
print(f'Found {len(high_value_sales_transactions)} high-value approved sales transactions')
 
# Query for reporting period
january_invoices = pl.Invoice.filter_by(
    q='attrs[billing_period] == "2024-01"'
).all()
 
print(f'Found {len(january_invoices)} invoices for January 2024')

Query by custom attributes using the same filter syntax as standard fields.

Query examples:

  • Find all sales department transactions
  • Get invoices for specific billing period
  • List accounts by customer segment
  • Search by external system IDs
  • Filter by project or cost center

Complex queries: Combine multiple attributes with standard fields:

// High-value approved sales transactions
q: 'attrs[department] == "Sales" && attrs[approval_code] != null && amount >= 1000.00'

// Enterprise invoices past due
q: 'attrs[customer_tier] == "enterprise" && status == "unpaid" && due_date < date("now")'

// Accounts by region with high churn risk
q: 'attrs[sales_region] == "US-West" && attrs[churn_risk] == "high"'

Dashboard and Reporting Integration


Custom attributes enhance dashboard visibility and reporting capabilities.

Dashboard display

Where attributes appear:

  • Object detail pages show all custom attributes (except underscore-prefixed)
  • Search includes custom attribute fields
  • Exports can be customized to include custom attributes

Dashboard features:

  • Searchable: Find records by any attribute value
  • Filterable: Create saved filters using attributes
  • Exportable: Export includes all custom attributes
  • Hidden fields: Attributes starting with underscore (_) are hidden from dashboard

Integration-only attributes:

Prefix attribute names with an underscore to hide them from the dashboard. These attributes are still queryable via API but won't appear in the dashboard UI:

{
  "attrs": {
    "customer_tier": "enterprise",
    "_crm_id": "sf_00123456",
    "_last_sync": "2024-01-15T10:30:00Z"
  }
}

Use underscore-prefixed attributes for system references, sync metadata, and internal fields that dashboard users don't need to see.

Receipts and Custom Attributes


Custom attributes on transactions automatically appear on receipts (except underscore-prefixed).

Transaction custom attributes are included in generated receipts, providing customers with complete context. Use display-friendly key names (e.g., "Order ID", "Project Code") instead of snake_case for better presentation on customer-facing receipts.

Receipt formatting best practices:

  • Use title case with spaces for customer-facing fields: "Order ID", "Project Code"
  • Underscore-prefixed attributes are always excluded from receipts

Example:

{
  "attrs": {
    "Order ID": "ORD-2024-5678",
    "Project Code": "PROJ-Q1",
    "_crm_id": "sf_00123456"
  }
}

For complete details on receipt generation and customization, see Transaction Receipts.

System Integration


Use custom attributes to integrate Payload with external systems.

import payload
pl = payload.Session('secret_key_3bW9...', api_version='v2')
from datetime import datetime
 
# Use custom attributes for system integration
 
# Sync transaction from external system
def sync_external_transaction(external_data):
    transaction = pl.Transaction.create(
        type='payment',
        amount=external_data['amount'],
        sender={
            'account_id': external_data['payload_account_id']
        },
        receiver={
            'account_id': external_data['merchant_account_id']
        },
        attrs={
            # External system reference
            '_external_id': external_data['transaction_id'],
            '_external_system': 'QuickBooks',
            '_external_url': f"https://quickbooks.com/transactions/{external_data['transaction_id']}",
 
            # Sync metadata
            '_sync_timestamp': datetime.now().isoformat(),
            '_sync_version': '2.0',
            '_sync_batch_id': external_data['batch_id'],
 
            # Preserve external data
            '_external_customer_id': external_data['customer_id'],
            '_external_invoice_number': external_data['invoice_number'],
            '_external_payment_method': external_data['payment_method'],
 
            # Reconciliation tracking
            '_reconciled': False,
            '_reconciliation_date': None,
            '_reconciliation_notes': None
        }
    )
 
    print(f'Synced transaction {transaction.id} from external system')
    return transaction
 
# Update reconciliation status
def mark_transaction_reconciled(transaction_id, notes):
    updated = pl.Transaction.get(transaction_id)
 
    updated.update({
        'attrs': {
            '_reconciled': True,
            '_reconciliation_date': datetime.now().isoformat(),
            '_reconciliation_notes': notes,
            '_reconciled_by': '[email protected]'
        }
    })
 
    print(f'Transaction {transaction_id} marked as reconciled')
    return updated
 
# Query unreconciled transactions
def get_unreconciled_transactions():
    unreconciled = pl.Transaction.filter_by(
        q='attrs[_reconciled] == false || attrs[_reconciled] == null',
        order_by='asc(created_at)'
    ).all()
 
    print(f'Found {len(unreconciled)} unreconciled transactions')
    return unreconciled
 
# Example usage
external_data = {
    'transaction_id': 'QB-TXN-12345',
    'amount': 1500.00,
    'payload_account_id': 'acct_customer123',
    'merchant_account_id': 'acct_merchant456',
    'batch_id': 'BATCH-2024-01-15',
    'customer_id': 'QB-CUST-999',
    'invoice_number': 'INV-2024-001',
    'payment_method': 'Credit Card'
}
 
sync_external_transaction(external_data)

Store external system references and synchronization metadata using underscore-prefixed attributes to keep integration fields hidden from the dashboard.

External system references

Common integration attributes:

Use underscore prefix for integration-only fields:

  • _external_id - External system IDs (CRM, ERP, accounting)
  • _external_url - External URLs for deep linking
  • _sync_timestamp - Last sync timestamp
  • _sync_batch_id - Sync batch or job ID
  • _source_system - Source system identifier

Bi-directional sync tracking

Track synchronization with hidden attributes:

  • _last_sync - Last sync timestamp
  • _sync_version - Sync version or revision number
  • _sync_status - Sync status (completed, pending, failed)
  • _sync_batch_id - Sync batch or job ID
  • _source_system - Source system identifier

Example with both visible and hidden attributes:

{
  "attrs": {
    "order_id": "ORD-2024-5678",
    "_crm_account_id": "sf_00123456",
    "_last_sync": "2024-01-15T10:30:00Z",
    "_sync_status": "completed"
  }
}

Reconciliation tracking

Reconciliation attributes:

  • _reconciliation_status - Reconciliation status flags
  • _reconciliation_date - Reconciliation date and time
  • _reconciled_by - Reconciled by (user or system)
  • _reconciliation_notes - Reconciliation notes
  • _match_confidence - Matching confidence scores

Next Steps

Leverage custom attributes for enhanced data management and reporting


Use Attributes in Reporting

Build powerful reports with Reporting Overview for dashboard analytics, master advanced filtering with Building Report Queries using custom attribute filters, and explore Report Examples for common reporting patterns using custom attributes.

Integrate with External Systems

Connect external systems with System Integration best practices, receive real-time updates via Webhooks for automatic sync, and explore complete API capabilities in API Reference for integration development.

Customize Transaction Receipts

Include custom attributes on receipts with Transaction Receipts documentation, understand the full lifecycle with Transaction Status, and learn about bank statement customization with Custom Descriptors.


Related articles