> ## Documentation Index
> Fetch the complete documentation index at: https://developers.activeprospect.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Submit feedback for a lead using query parameters

> An alternative to `POST /feedback` for senders that cannot issue a request body. The
feedback fields are read from the query string alongside `event_id`. Behavior is
otherwise identical to the `POST` form, including the caveat that a `201` reports the
submission was processed, not that it was accepted.




## OpenAPI

````yaml /api-reference/leadconduit_api.json get /feedback
openapi: 3.1.0
info:
  title: LeadConduit API
  version: 1.0.1
  termsOfService: https://activeprospect.com/terms-of-service/
  contact:
    name: ActiveProspect Support Team
    email: support@activeprospect.com
    url: https://support.activeprospect.com
  description: |
    # About
    This documentation is a technical manual for the LeadConduit API. It is
    primarily intended to be read by software developers who want to integrate
    another system with LeadConduit.

    The API is RESTful and speaks JSON over HTTPS. In general, anything that
    can be accomplished by pointing and clicking in the LeadConduit user
    interface can also be done using the API.

    ## Conventions
    This documentation provides examples that show how to interact with the
    API. Examples are shown in monospaced blocks like the one shown below:

    ```
    Examples are presented in blocks like this one
    ```

    You may also see monospaced font inline `like this`. This text represents a
    literal value or expression to be used with the API.

    ## Questions
    If you have a question, or spot an error in the documentation, or just
    generally need to speak with someone about the API, email
    [support@activeprospect.com](mailto:support@activeprospect.com).
servers:
  - url: https://app.leadconduit.com
    description: The base URL for production.
security:
  - APIKey: []
tags:
  - name: Feedback
    description: >
      Feedback lets lead recipients report what happened to a lead after
      delivery: a conversion when the lead progressed through a lifecycle stage,
      or a return when the lead is given back. Feedback events power conversion
      reporting, including the per-stage columns on the Connections Report.
  - name: Destinations
    description: >-
      Destinations are reusable configurations that define where and how lead
      data is sent to external systems after processing in a flow. They
      represent the final delivery point for qualified leads, whether that's a
      CRM, marketing automation platform, partner system, or any other business
      application.


      ## Key Concepts


      ### What Are Destinations?


      Destinations are pre-configured connections to external systems that
      receive lead data. Unlike recipient steps that are configured individually
      within each flow, destinations are configured once at the account level
      and can then be reused across multiple flows. This "configure once, use
      many times" approach ensures consistency and reduces setup time.


      ### Powered by Prismatic


      Destinations are powered by Prismatic, an embedded integration platform
      that provides:

      - A marketplace of pre-built integrations for popular business systems

      - Secure credential management

      - Field mapping capabilities

      - Multiple actions per destination (e.g., "Create Lead", "Update Contact",
      "Add to Campaign")


      ## Configuration Process


      Destinations must be configured through the LeadConduit application
      interface because the setup process requires:


      1. **Integration Selection**: Browse and select from a marketplace of
      pre-built integrations (Salesforce, HubSpot, Marketo, custom webhooks,
      etc.)


      2. **Authentication Setup**: Securely configure credentials for the
      external system, which may involve:
         - OAuth authorization flows
         - API key management
         - Username/password authentication
         - Custom authentication headers

      3. **Instance Configuration**: Define specific settings for your instance
      of the integration:
         - Which Salesforce organization to connect to
         - Which HubSpot account to use
         - Which mailing lists or campaigns to target
         - Custom endpoint URLs for webhooks

      4. **Field Mapping**: Interactively map LeadConduit fields to destination
      fields:
         - Match lead fields to CRM fields
         - Set default values
         - Configure data transformations
         - Handle custom fields

      5. **Testing & Validation**: Verify the connection works properly before
      using in production flows


      ## Using Destinations in Flows


      Once configured, destinations can be:

      - Queried via the API to see all available destinations

      - Added to flows programmatically via the API

      - Reused across multiple flows without reconfiguration

      - Updated centrally with changes reflected in all flows using them


      ### Delivery Rules


      - Lead data is only sent to a destination if all prior flow steps complete
      successfully

      - Any error in preceding steps will prevent delivery to protect data
      quality

      - Each destination can support multiple actions, allowing different
      operations on the same system


      ### Multiple Actions


      A single destination can offer multiple actions. For example, a Salesforce
      destination might provide:

      - "Create New Lead" - Adds a new lead record

      - "Update Existing Contact" - Updates contact information

      - "Add to Campaign" - Enrolls the lead in a marketing campaign

      - "Create Opportunity" - Converts qualified leads to opportunities


      ## API Capabilities


      While destinations cannot be created or modified via the API, you can:

      - List all configured destinations for your account

      - View destination details including available actions

      - See which flows use each destination (with `include=flow_usages`
      parameter)

      - Add existing destinations to flows

      - Remove destinations from flows
  - name: Flows
    description: >
      Flows define the complete processing pipeline for lead submissions, from
      data ingestion through delivery. Each flow creates a configurable handler
      that processes leads through a series of middleware steps in strict
      sequence.


      ## Flow Processing Architecture


      ### Lead Submission Process

      When a lead is submitted to a flow's source endpoint, it passes through a
      predetermined middleware chain organized into distinct phases:


      **Setup:**

      1. **Lead submission** - Request received

      2. **Authentication** - Verify source credentials and permissions


      **Inbound Request Processing:**

      1. **Inbound integration** - Execute source-specific request handling

      1. **Data hygiene** - Clean and validate field names and values

      1. **Inbound mapping** - Transform source field names to flow field names

      1. **Lead object creation** - Parse data using configured field types


      **Inbound Validation:**

      1. **Suppression list checks** - Verify against do-not-contact lists

      2. **Pricing calculation** - Apply source and flow pricing rules

      3. **Caps validation** - Check submission limits

      4. **Acceptance criteria** - Evaluate business rules for lead acceptance


      **Steps Execution:**

      1. **Step processing** - Execute configured filter and recipient steps
      sequentially

      2. **Revenue calculation** - Calculate final pricing and revenue

      1. **Inbound integration** - Execute source-specific response 

      4. **Respond** - Return the integration response to source


      ### Multi-Source Configuration

      Flows accommodate multiple data sources through the `sources` array. Each
      source has:

      - **Entity reference** - Identifies the submitting party

      - **Inbound mappings** - Field name transformations specific to that
      source

      - **Authentication settings** - Credentials and security requirements

      - **Pricing configuration** - Source-specific cost calculations


      ### Step-Based Processing

      The `steps` array defines sequential processing after lead acceptance.
      Steps are combined with any configured `destinations` and executed in
      order.


      **Step Types:**

      - **Filter steps** - Apply business logic, data enhancement, routing
      decisions, or flow control

      - **Recipient steps** - Deliver leads to external systems (CRMs, buyers,
      etc.)


      **Step Execution Details:**

      - Steps execute sequentially, one at a time

      - Each step can access and modify the lead data

      - Disabled steps (`enabled: false`) are skipped but counted

      - Filter steps can terminate the entire flow early using rules

      - Each step generates an event record with timing and outcome data

      - Step failures don't stop subsequent steps unless the step explicitly
      exits the flow

      - The lead object accumulates data from all steps, making enhanced data
      available to later steps


      **Recipient Step Processing Pipeline:**

      Recipient steps mirror the inbound validation structure but in reverse -
      transforming data for outbound delivery:


      1. **Integration lookup** - Load the outbound integration module

      2. **Rules evaluation** - Check if step should execute (skip if rules
      fail)

      3. **Outbound mapping** - Transform lead data for recipient requirements

      4. **Type parsing** - Convert data to integration's expected types

      5. **Integration validation** - Verify prerequisites are met

      6. **Caps checking** - Apply step-level volume limits

      7. **HTTP request execution** - Send data to external system

      8. **Response processing** - Parse integration response

      9. **Data appending** - Add response data back to lead object

      10. **Event recording** - Save step execution details and outcome


      **Step Configuration:**

      - **Entity reference** - Defines which integration module to use

      - **Mappings** - Transform lead data for the specific recipient

      - **Rules** - Conditional logic determining when the step executes

      - **Enabled flag** - Allows steps to be temporarily disabled


      ### Configuration Components

      Flows are composed of:

      - **Fields array** - Defines expected data schema and types

      - **Sources array** - Configures data input endpoints

      - **Steps array** - Defines post-acceptance processing

      - **Acceptance criteria** - Rules determining lead purchase decisions

      - **Pricing configuration** - Cost calculation logic

      - **Caps** - Volume and rate limiting controls
  - name: Fields
    description: >
      Fields define your lead data model in LeadConduit. They specify what
      information you collect, how it's structured, and what types of data are
      valid. Think of fields as the columns in your lead database - each field
      represents a specific piece of information about a lead, from basic
      contact details to complex business-specific data.


      ## Why Fields Matter


      Without a consistent data model, lead processing becomes chaos. Every
      vendor sends different field names, every system expects different
      formats, and data quality suffers. Fields solve this by:


      - **Standardizing Data** - Common fields work the same way everywhere

      - **Enforcing Types** - Each field knows what kind of data it holds

      - **Enabling Validation** - Invalid data is caught and flagged

      - **Supporting Intelligence** - Typed fields provide parsed components


      ## Standard vs Custom Fields


      LeadConduit provides two categories of fields:


      ### Standard Fields


      Pre-defined fields for common lead data:


      | Category        | Examples                                             |
      Purpose                  |

      |-----------------|------------------------------------------------------|--------------------------|

      | **Contact**     | Email, Phone 1, Phone 2, Phone 3                     |
      Core contact information |

      | **Identity**    | First Name, Last Name, Age, Gender                   |
      Personal identification  |

      | **Location**    | Address 1, Address 2, City, State, Postal Code       |
      Home Address             |

      | **Many Others** | Hundreds of standard fields by vertical and use case |
      Misc.                    | 


      A built-in standard field catalog provides consistent field names and
      normalized values

      across all flows and accounts dramatically reducing the operational cost
      of maintaining

      data integrations between parties. 


      **We strongly encourage you to use a standard field instead of create a
      custom field.**


      ### Custom Fields


      Fields created to address needs unique needs:

      - Common lead information not availalbe as a standard field (let us know
      so we can add it to our catalog!)

      - Proprietary or internal tracking information

      - Lead seller specific tracking information


      Custom fields are just as powerful as standard fields - they support all
      the same types and validation. Nevertheless, 

      **try to avoid using custom fields if you can**. It will make technical
      setup and maintenance much easier.


      #### Field Suffixes


      Field suffixes are a namespacing mechanism that prevents data collision
      across accounts. When multiple accounts create custom fields, suffixes
      ensure uniqueness in naming.


      **Example scenario:**

      - Account A adds a field called "score"

      - Account B also wants a field called "score"

      - Their account suffixes are: `accta` and `acctb`

      - The field IDs are `score_accta` and `score_acctb`

      - If Account A wants to deliver a lead to Account B, a manual mapping must
      be added: `score_accta` -> `score_acctb`

      - Account A must ensure that Account B's `score` field has the same
      meaning. Account A's score field might represent credit score, while
      Account B's score field might represent lead score.

      - The suffix prevents automatic field mapping from making mistakes. 


      **Suffix Naming Rules:**

      - 3-5 alphanumeric characters

      - Lowercase only

      - Must be unique across all entities

      - Optional but recommended for entities with custom fieldsEvery custom is
      uniquely named across the platform. 



      ## Field Types and Intelligence


      The real power of fields comes from their [types](types.md). Each field
      has a type that determines how data is parsed, validated, and enriched:


      ### Example: Phone Field

      ```

      Raw Input: "(555) 123-4567 ext 890"


      Parsed Result:

      - phone_1: "5551234567"

      - phone_1.area: "555"

      - phone_1.exchange: "123"

      - phone_1.line: "4567"

      - phone_1.extension: "890"

      - phone_1.valid: true

      - phone_1.mobile: false

      ```


      ## Using Fields in Flows


      Fields interact with several flow components:


      ### 1. Source Configuration

      When configuring a source, you map vendor fields to your standard fields:


      | Vendor Sends    | Maps To       |
      Result                                     |

      |-----------------|---------------|--------------------------------------------|

      | `contact_email` | `email`       | Vendor's data fills your email
      field       |

      | `primary_phone` | `phone_1`     | Vendor's data fills your phone_1
      field     |

      | `loan_amt`      | `loan_amount` | Vendor's data fills your loan_amount
      field |


      ### 2. Validation Rules

      Fields work with [rules](rules.md) for validation:

      - "lead.email is valid"

      - "lead.phone_1 is not blank"

      - "lead.loan_amount is between 5000 and 100000"


      ### 4. Recipient Mapping

      When configuring an outboudn integration, fields map to downstram system
      requirements:


      | Your Field  | Recipient Expects  |
      Result                                    |

      |-------------|--------------------|-------------------------------------------|

      | `email`     | `Email_Address__c` | Your email becomes their
      Email_Address__c |

      | `phone_1`   | `PrimaryPhone`     | Your phone_1 becomes their
      PrimaryPhone   |


      ## Field Namespaces


      Fields exist in different namespaces depending on context. It's important
      to know that

      data collected during a lead flow does sits outside the `lead.*`
      namespace, which is 

      reserved for data that's submitted by the source into the flow. 


      **Appended data does not require a field in the flow.** Instead, appended
      data is dynamic

      and "shaped" as it comes back from recipient step processing.


      | Namespace    | Contains                                 |
      Example                        |

      |--------------|------------------------------------------|--------------------------------|

      | (no prefix)  | Custom fields                            | `loan_type`,
      `credit_score`    |

      | `lead.*`     | All field values submitted with the lead | `lead.email`,
      `lead.phone_1`   |

      | `appended.*` | Enhancement data                         |
      `appended.demographics.income` |



      ## Best Practices


      ### Choosing Fields


      **Do:**

      - Use standard fields when they fit your needs

      - Create custom fields only when necessary

      - Use descriptive names for custom fields

      - Include units in field names when relevant (e.g., `loan_amount_usd`)


      **Don't:**

      - Duplicate standard fields with custom versions

      - Use generic names like `field1`, `custom1`

      - Store multiple values in one field

      - Ignore field types - use the right type for your data


      - **[Types](types.md)** - How fields parse and validate data

      - **[Mappings](mappings.md)** - How fields transform between systems

      - **[Rules](rules.md)** - How fields are used in business logic

      - **[Templates](templates.md)** - How to access field values dynamically
  - name: Exports
  - name: Packages
    description: >-
      Integration packages available to the account, with their pricing and
      access details.
  - name: Leads
    description: >-
      Free accounts do not have access to Leads
      (https://app.leadconduit.com/leads) and will receive a 401 (Unauthorized) 

      error when trying to access it
  - name: Entities
    description: >-
      Entities are the foundation of identity in LeadConduit. They represent the
      "who" in every lead transaction - who sends leads, who receives them, and
      what systems are involved. Think of entities as the business cards of the
      LeadConduit ecosystem - they establish identity, capabilities, and
      relationships.


      At their core, entities answer two fundamental questions:

      1. **What does the entity represent?** (Identity)

      2. **How can it integrate with LeadConduit?** (Capability)

      3. **How am I interacting with the entity through Leadconduit?**
      (Relationships)


      ## Why Entities Exist


      ### The Identity Problem

      LeadConduit needs to track:


      - Companies selling leads

      - Companies buying leads  

      - Web forms collecting leads

      - CRM systems receiving leads

      - Analytics platforms tracking leads

      - Enhancement services enriching leads


      Without a unified identity system, this becomes chaos. Entities solve this
      by providing a consistent way to represent any participant in the lead
      ecosystem.


      ### The Capability Problem

      Different systems have different capabilities:

      - A web form can send leads but not receive them

      - A CRM can receive leads but typically doesn't send them 

      - A partner company might both send and receive leads

      - Each system supports different integration methods


      Entities encode these capabilities through their type classifications and
      module associations.


      ### The Relationship Problem

      Business relationships are complex:

      - You might buy leads from Company A

      - You might sell leads to Company B

      - Company C might be both a buyer and seller


      Entities model these relationships, especially through the account entity
      and connection system.


      ## Core Concepts


      ### Entity Classification


      Every entity has two fundamental classifications that define its role:


      **Source Classification** (Can this entity send leads?)

      - `seller` - A company that sells leads

      - `form` - A web form or landing page

      - `other` - Any other type of lead source

      - `null` - Cannot send leads


      **Recipient Classification** (Can this entity receive leads?)

      - `buyer` - A company that purchases leads

      - `crm` - Customer Relationship Management system

      - `analytics` - Analytics or tracking platform (including internal scoring
      services)

      - `enhancement` - Data enrichment service

      - `esp` - Email Service Provider

      - `other` - Any other type of recipient

      - `null` - Cannot receive leads


      An entity can be both a source and recipient. For example, a partner
      company might be classified as both `source: "seller"` and `recipient:
      "buyer"`.


      **Important Note**: Entities can represent both business relationships AND
      technical systems. For example, a company might create an entity called
      "Scoring Service" with `recipient: 'analytics'` to represent their
      internal scoring API. This allows them to track all requests to that
      service across multiple flows.


      ### Entity Types by Management


      **Account Entities**

      - Special entities representing LeadConduit account holders

      - Enable account-to-account lead sharing

      - Automatically created when an account is created

      - Can form connections with other accounts

      - Always have both source and recipient capabilities


      **Standard Entities**

      - Pre-built by LeadConduit

      - Represent well-known companies (that do not have an account) and
      well-known services/platforms

      - Available to all accounts

      - Cannot be modified by users

      - Examples: Major CRMs (Salesforce, HubSpot), big lead sellers, popular
      form-builder services


      **Custom Entities**

      - Created by individual accounts when there is no account entity or
      standard entity

      - Represent unique business relationships

      - Only editable by the creating account

      - Example: Proprietary scoring system, third party data services with no
      built-in integration


      **Pending Entities**

      - Temporary entities created during the connection invitation process

      - Convert to regular entities once invitation is accepted

      - Used to pre-configure flows before partners join


      ### The Module System


      Modules are capability declarations that define HOW an entity can
      technically communicate. A `module_id` is a unique identifier for a
      specific integration (e.g., "Create Salesforce Lead", "Send to HubSpot
      Contact", "LendingTree Ping Post"). Each entity has a `module_ids` array
      that acts as both a capability list and a constraint system.


      **How Module Constraints Work**:

      - When `module_ids` is non-empty, the entity can ONLY be configured with
      those specific integrations

      - When adding the entity to a flow, users can only select from the
      supported modules

      - This prevents configuration errors and ensures compatibility


      **Real-World Example - LendingTree Entity**:

      LendingTree has proprietary lead submission requirements that only work
      with specific integrations. Their entity's `module_ids` array contains
      only the modules that support their format:

      ```json

      {
        "name": "LendingTree",
        "module_ids": [
          "leadconduit-lendingtree.inbound.post",
          "leadconduit-lendingtree.inbound.ping_post"
        ]
      }

      ```


      For example, adding LendingTree as a flow source, only allows
      configuration of LendingTree-specific integration module IDs. This
      eliminates the possibility of trying to use a generic HTTP module that
      wouldn't understand LendingTree's proprietary format.


      **Module Types**:

      - **Standard Integration**: Basic HTTP communication
      (`leadconduit-default.inbound/outbound`)

      - **Third-Party Integrations**: Vendor-specific integrations
      (`leadconduit-salesforce.outbound`)  

      - **Generic Integration**: Format-specific implementations
      (`leadconduit-custom.outbound.json`)

      - **Internal Platform Integrations**: LeadConduit-to-LeadConduit
      (`leadconduit-integration-leadconduit.outbound.to_buyer`)


      **Empty vs Non-Empty Arrays**:

      - Empty `module_ids`: Entity can use any available integration module

      - Non-empty `module_ids`: Entity is restricted to only those specific
      modules


      ### The Reverse Integration Pattern


      One of LeadConduit's most powerful features is how it handles
      vendor-specific integrations through entities. This flips the traditional
      integration model on its head.


      **Traditional Lead Industry Model:**

      - Buyer publishes submission specifications

      - Every seller must implement the buyer's API

      - Sellers do custom development for each buyer

      - Technical burden is on the seller


      **LeadConduit's Entity-Based Model:**

      - Major vendors have custom integration modules

      - These modules are associated with the vendor's entity

      - Buyers simply select the vendor's entity as a source

      - LeadConduit handles all the technical complexity


      **Real-World Example:**

      A major lead vendor requires:

      - Proprietary authentication scheme

      - Custom data format with specific field names

      - Special validation rules

      - Unique error handling


      Instead of every buyer implementing this vendor's requirements:

      1. The vendor gets a LeadConduit account (creating an account entity)

      2. Their custom integration module is associated with their account entity

      3. Any buyer can now receive leads from them by adding that entity as a
      source

      4. The buyer selects the vendor's entity, and LeadConduit automatically
      uses the correct integration


      This approach is particularly valuable because:

      - Large vendors often have the market power to dictate technical
      requirements

      - They want to make it easy for buyers to work with them

      - Buyers don't need technical resources to integrate with complex vendors

      - Integration happens once, benefits everyone


      **The Shift to Account Entities:**

      LeadConduit is moving away from standard entities in favor of requiring
      all participants to have accounts. This means:

      - Every major vendor will have their own account entity

      - Custom integrations are attached to these account entities

      - Better accountability and relationship tracking

      - More flexibility for vendors to manage their own integration
      requirements


      ### Connection System


      For account entities (representing other LeadConduit accounts), the
      connection system tracks business relationships:


      **Connection Types**

      - `seller`: They sell leads to you

      - `buyer`: They buy leads from you

      - `both`: Bidirectional relationship

      - `null`: No active connection


      Connections enable:

      - Simplified account-to-account lead sharing

      - Relationship visibility in the UI

      - Access control for lead flow

      - Business relationship tracking


      ## Entity Lifecycle


      ### Creation

      When an entity is created, it establishes:

      1. **Identity**: Name, description, logo, website

      2. **Classification**: What type of source/recipient

      3. **Capabilities**: Which modules it supports

      4. **Ownership**: Which account owns it (for custom entities)


      ### Usage

      Entities are used in flows by:

      1. **Adding as a source**: Defines where leads come from

      2. **Adding as a recipient**: Defines where leads go

      3. **Configuring integration**: Using the entity's supported modules

      4. **Applying business rules**: Based on the entity relationship


      ### Updates

      When entities are updated:

      1. Changes propagate to all flows using the entity

      2. Historical data maintains the entity reference

      3. Active flows automatically use new entity metadata

      4. No disruption to lead processing


      ### Deprecation

      Instead of deletion, entities can be deprecated:

      1. Marked with a deprecation flag

      2. Can reference a replacement entity

      3. Existing usage continues to work

      4. New usage is discouraged or prevented


      ## Special Entity Behaviors


      ### Enhancement Recipients

      Enhancement services (data append, verification, etc.) are special:

      - They receive lead data for processing

      - They don't consume leads like buyers do

      - They're excluded from certain account relationship tracking

      - They typically return enriched data back to the flow


      ### Account Entity Auto-Creation

      When a LeadConduit account is created:

      1. An entity is automatically created with the same ID

      2. It's marked as both source and recipient

      3. It gets standard B2B communication modules

      4. It represents that account in the entity ecosystem


      ### Module Inheritance

      Entities inherit certain behaviors from their modules:

      - Authentication requirements

      - Configuration options

      - Data format expectations

      - Integration-specific features


      ## Business Implications


      ### For Lead Buyers

      Entities representing buyers:

      - Track which companies purchase your leads

      - Enable consistent delivery configuration

      - Maintain delivery history

      - Support buyer-specific customization


      More importantly for buyers receiving leads:

      - **No custom development needed** for complex vendors

      - **Automatic integration** with major lead sources

      - **Reduced technical burden** - just select the vendor's entity

      - **Faster onboarding** with new lead sources

      - **Consistent experience** across all vendors


      ### For Lead Sellers  

      Entities representing sellers:

      - Identify lead sources clearly

      - Enable source-specific validation

      - Track source quality metrics

      - Support seller-specific pricing


      For major vendors with custom requirements:

      - **Control their integration** specifications

      - **Implement once** for all buyers on LeadConduit

      - **Reduce buyer friction** by eliminating technical barriers

      - **Maintain standards** while being easy to work with

      - **Scale distribution** without buyer-by-buyer integrations


      ### For Integrations

      Entities representing systems:

      - Standardize integration configuration

      - Enable reuse across flows

      - Maintain consistent behavior

      - Simplify credential management


      **Key distinction**: While entities enable configuration reuse across
      flows, each flow must still be independently configured to connect to the
      service. The entity provides:

      - **Identity tracking** - Know which flows use the service

      - **Usage analytics** - Track requests across all flows

      - **Consistent naming** - Same entity name in all flows

      - **Classification** - Properly categorize the service type


      For example, a "Scoring Service" entity:

      - Represents your internal lead scoring API

      - Used in multiple flows for consistency

      - Each flow independently configures the API endpoint and credentials

      - Allows tracking all scoring requests across your account


      The reverse integration pattern means:

      - **Vendors define requirements** instead of conforming to each buyer

      - **LeadConduit implements** the vendor's specifications

      - **Buyers benefit** without doing any integration work

      - **Market power** translates to technical convenience


      ### For Partner Relationships

      Account entities enable:

      - Direct account-to-account lead sharing

      - Trusted business relationships

      - Simplified technical integration

      - Relationship visibility

      - Custom integration attachment


      ## Common Patterns


      ### Multi-Role Entities

      Many entities serve multiple roles:

      ```

      Partner Company ABC

      ├── As a seller: Sends leads to you

      ├── As a buyer: Receives leads from you

      └── As an account: Has their own LeadConduit instance

      ```


      ### Technical Service Entities

      Entities often represent internal technical services:

      ```

      Internal Services

      ├── Scoring Service (analytics recipient)

      ├── Data Validation API (enhancement recipient)

      ├── Lead Router (other recipient)

      └── Internal CRM (crm recipient)

      ```


      Each service entity:

      - Has consistent naming across flows

      - Enables tracking of all requests

      - Requires independent configuration per flow

      - Groups related technical integrations


      ### Integration Patterns

      Entities often follow integration patterns:

      ```

      CRM Integration Pattern:

      ├── Standard CRM Entity (e.g., Salesforce)

      ├── Multiple flows use the same entity

      ├── Each flow configures differently

      └── All share core CRM capabilities

      ```


      ## Best Practices


      ### Naming Conventions

      - Use clear, recognizable names

      - Include company/system name

      - Avoid generic names like "CRM" or "Buyer"

      - Consider future growth (avoid "Test" or "Temp")


      ### Classification Selection

      - Choose the most specific type available

      - Use "other" only when no specific type fits

      - Consider primary business relationship

      - Don't force-fit wrong classifications


      ### Module Selection

      - Only include actually supported modules

      - Don't add modules "just in case"

      - Consider authentication requirements

      - Match modules to business needs


      ### Relationship Management

      - Use standard entities when available

      - Create custom entities for unique relationships

      - Leverage account entities for B2B partnerships

      - Document special configurations in description


      ## Common Misconceptions


      ### "Entities are just labels"

      Reality: Entities carry configuration, capabilities, and relationship
      data. They're active participants in lead flow, not passive labels.


      ### "I need a new entity for each flow"

      Reality: Entities should be reused across flows. Create once, use many
      times.


      ### "Standard entities can't be customized"

      Reality: While you can't change standard entities, you can configure their
      integrations, acceptance criteria, volume caps, pricing, etc differently
      in each flow.


      ### "Account entities are complicated"

      Reality: Account entities simply represent another account on the
      platform. Each account only has one account entity, platform wide.


      ## Key Takeaways


      1. **Entities are Identity**: They establish who participates in lead
      transactions

      2. **Classification Drives Behavior**: Source and recipient types
      determine capabilities

      3. **Modules Define Integration**: The technical "how" of lead movement

      4. **Reverse Integration is Powerful**: Vendors define specs, LeadConduit
      implements, buyers benefit automatically

      5. **Account Entities are the Future**: Moving away from standard entities
      to account-based participation

      6. **Relationships Matter**: Especially for account-to-account lead
      sharing

      7. **Reuse is Key**: Create entities once, use across multiple flows

      8. **Custom Integrations Attach to Entities**: Major vendors get their own
      integration modules

      9. **Dual Purpose**: Entities can represent both business relationships
      AND technical services
  - name: Firehose
    description: >-
      # Firehose Multi-Service Configuration


      ## Overview


      Each flow is separately configured to use the firehose. This API allows
      firehose configuration to be tested before it 

      is set on a flow.


      The Firehose feature supports multiple cloud storage services for event
      data export. This document describes the configuration options for both
      AWS S3 and Azure Blob Storage services.


      ## Configuration Structure


      ### Legacy Configuration (Backward Compatible)


      For existing flows, the legacy configuration continues to work:


      ```json

      {
        "firehose": {
          "enabled": true,
          "credential_id": "507f1f77bcf86cd799439011",
          "bucket": "my-s3-bucket",
          "prefix": "events/production"
        }
      }

      ```


      ### New Service-Based Configuration


      The new service-based configuration allows multiple cloud storage
      providers:


      ```json

      {
        "firehose": {
          "enabled": true,
          "services": {
            "aws": {
              "enabled": true,
              "credential_id": "507f1f77bcf86cd799439011",
              "bucket": "my-s3-bucket",
              "prefix": "events/aws"
            },
            "azure": {
              "enabled": true,
              "credential_id": "507f1f77bcf86cd799439012", 
              "bucket": "my-azure-container",
              "prefix": "events/azure"
            }
          }
        }
      }

      ```


      ## Credential Validation


      ### AWS S3 Validation


      The `/firehose` endpoint validates AWS credentials by:


      1. Creating a test file with unique name:
      `leadconduit_verification_[flow_id_]YYYYMMDDHHMMSSMS.txt`

      2. Uploading it to the specified bucket (with optional prefix) when
      `verification_file=true` (default)

      3. Alternatively, validating bucket access without creating file when
      `verification_file=false`

      4. Returning a `{ validated: true, verification_file: <bool> }` payload on
      success


      **Required IAM permissions:**


      | Flow | Required permission |

      |------|---------------------|

      | `verification_file=true` (default) | `s3:PutObject` on
      `arn:aws:s3:::<bucket>[/<prefix>]/*` |

      | `verification_file=false` | `s3:ListBucket` on `arn:aws:s3:::<bucket>` |


      The default flow does **not** require `s3:ListBucket` -- a write-only IAM
      policy is sufficient

      for customers who want to limit LeadConduit's bucket-level visibility.


      **Error responses** follow the structured `LCError` schema. Upstream S3
      errors are mapped onto

      specific HTTP statuses (404 missing bucket, 403 IAM, 401 bad signature,
      400 wrong region,

      502 upstream/network failure) rather than a generic 500. See the OpenAPI
      path file for the

      full mapping.


      **Example Requests:**


      *Basic validation (creates file):*

      ```bash

      curl -X GET
      "https://app.leadconduit.com/firehose?service=aws&access_key_id=AKIA...&secret_access_key=wJal...&bucket=my-bucket&prefix=test"

      ```


      *With flow ID (includes flow identifier in filename):*

      ```bash

      curl -X GET
      "https://app.leadconduit.com/firehose?service=aws&access_key_id=AKIA...&secret_access_key=wJal...&bucket=my-bucket&flow_id=507f1f77bcf86cd799439011"

      ```


      *Validation only (no file created):*

      ```bash

      curl -X GET
      "https://app.leadconduit.com/firehose?service=aws&access_key_id=AKIA...&secret_access_key=wJal...&bucket=my-bucket&verification_file=false"

      ```


      **Example Responses:**


      *File created:*

      ```json

      {
        "validated": true,
        "verification_file": true
      }

      ```


      *Validation only:*

      ```json

      {
        "validated": true,
        "verification_file": false
      }

      ```


      ### Azure Blob Storage Validation


      The `/firehose` endpoint validates Azure credentials by:


      1. Creating a BlobServiceClient from the connection string

      2. Checking if the specified container exists and is accessible

      3. Optionally creating verification file based on `verification_file`
      parameter

      4. Returning container validation information


      **Example Request:**

      ```bash

      curl -X GET
      "https://app.leadconduit.com/firehose?service=azure&connection_string=DefaultEndpointsProtocol=https;AccountName=test;AccountKey=key;EndpointSuffix=core.windows.net&bucket=my-container"

      ```


      **Example Responses:**


      *With verification file:*

      ```json

      {
        "validated": true,
        "verification_file": true
      }

      ```


      *Validation only (verification_file=false):*

      ```json

      {
        "validated": true,
        "verification_file": false
      }

      ```


      ## Event Processing


      ### Single Service Configuration


      When only one service is configured, events are sent to that service. If
      the service fails, events are spooled for retry.


      ### Multi-Service Configuration


      When multiple services are configured:


      1. Each enabled service receives the event independently

      2. If a service fails, it generates its own spool message for retry

      3. Each service can have different bucket/container and prefix
      configurations


      ### Spooling and Retry


      - Each service failure generates a separate SQS message for retry

      - Spooled events contain service-specific metadata (bucket, prefix,
      credentials)

      - The unspooler processes each service's failed events independently

      - File-based spooling creates separate paths for each service
      configuration


      ## Migration Guide


      ### From Legacy to Service-Based Configuration


      1. **Keep existing configuration**: Legacy configuration continues to work

      2. **Add services object**: Gradually migrate to service-based
      configuration

      3. **Test thoroughly**: Validate both configurations work as expected


      Example migration:


      **Before:**

      ```json

      {
        "firehose": {
          "enabled": true,
          "credential_id": "507f1f77bcf86cd799439011",
          "bucket": "my-bucket"
        }
      }

      ```


      **After:**

      ```json

      {
        "firehose": {
          "enabled": true,
          "services": {
            "aws": {
              "enabled": true,
              "credential_id": "507f1f77bcf86cd799439011",
              "bucket": "my-bucket"
            }
          }
        }
      }

      ```
  - name: Account
    description: >
      The authenticated user reveals the details of the API caller's user
      account as well as certain details about

      the account of which the user is member. The `products` array reveals all
      ActiveProspect products and to which the

      caller's account is subscribed.
  - name: Connections
    description: Account connections with other LC accounts
  - name: System
  - name: Changelogs
    description: |
      A changelog represents a creation, update, or deletion of a model such as
      `Flow`, `Entity`, or `Field`. Every change made will log the exact
      difference between the previous revision and the current one, as well as
      what user was logged in and what account the change was made on.
      Contained within the changelog is the full revision of the model changed
      at the state after the change was applied, as well as the differences
      between the previous revision and current revision.
  - name: Reports
    description: ''
  - name: Routers
    description: >
      A router receives leads at a single URL and decides which flow and source
      should handle

      each one. It is useful when the same lead format arrives for many
      campaigns, or when a

      partner can only post every lead to one endpoint: LeadConduit picks the
      destination by

      looking at the lead itself.


      A router has an inbound integration, which parses the incoming request,
      and an ordered

      list of routes. Each route pairs a rule set with a flow and a source. When
      a lead arrives

      at `POST /routers/{router_id}/route`, the rules of every route are
      evaluated against the

      parsed lead and the last matching route wins. The lead is then submitted
      to that flow and

      source exactly as if it had been sent to
      `/flows/{flow_id}/sources/{source_id}/submit`.

      Add `dry_run=true` to the request to see which route would match without
      submitting

      anything.
  - name: Types
    x-traitTag: true
    description: >+
      - [Overview](#overview)

      - [Why Types Exist](#why-types-exist)

      - [Core Concepts](#core-concepts)

      - [Quick Reference](#quick-reference)

      - [Complete Type Catalog](#complete-type-catalog)
        - [Contact Types](#contact-types)
        - [Personal Data Types](#personal-data-types)
        - [Location Types](#location-types)
        - [Data Types](#data-types)
        - [Specialized Types](#specialized-types)
      - [How Types Work in Practice](#how-types-work-in-practice)

      - [Special Type Behaviors](#special-type-behaviors)

      - [Type Selection Guide](#type-selection-guide)

      - [Common Patterns](#common-patterns)

      - [Integration with LeadConduit](#integration-with-leadconduit)


      ## Overview


      Types are the data standardization engine of LeadConduit. They transform
      the messy, inconsistent data that arrives from various sources into clean,
      structured, and predictable formats that systems can reliably process.
      Think of types as intelligent parsers that not only validate data but also
      extract meaningful components and metadata.


      At their core, types solve three fundamental problems:

      1. **Data Chaos**: Lead data arrives in countless formats

      2. **Validation Confusion**: What makes data "valid" varies by context

      3. **Component Extraction**: Valuable data is often buried within strings


      ## How Types Work


      ### The Problems Types Solve


      **Format Chaos**: Lead data comes from everywhere with different formats -
      phone numbers as "(512) 789-1111" vs "512-789-1111" vs "5127891111",
      states as "TX" vs "Texas" vs "tx". This creates chaos both **inbound**
      (accepting leads) and **outbound** (delivering to external systems).


      **Validation Dilemma**: Traditional systems reject misformatted data, but
      in lead distribution you can't control input formats, rejecting leads
      means lost revenue, and different buyers have different requirements.


      **Hidden Value**: Valuable data is embedded within strings (area codes,
      domains, ZIP codes, age from DOB) requiring complex string manipulation to
      extract.


      ### How Types Solve These Problems


      **Parse-Normalize-Use Pattern**: Every type follows three steps: **Parse**
      (accept various formats), **Normalize** (convert to consistent format),
      **Use** (access standardized data and components in flows).


      Example: `"(512) 789-1111"` → `{ raw: "(512) 789-1111", normal:
      "5127891111", area: "512", exchange: "789", line: "1111", valid: true }`


      **Normalization Benefits**:

      - **Simplified Rules**: Write one rule for `State equals "TX"` instead of
      separate rules for `"tx"`, `"texas"`, `"Texas"`, etc.

      - **Effortless Outbound Delivery**: Send normalized data to external
      systems often without format conversion

      - **Consistent Storage**: All data stored in predictable formats


      **Component Extraction**: Types automatically extract valuable components
      without string manipulation:

      - **Phone**: Area code, exchange, line, extension, type, toll-free status

      - **Email**: User, domain, host, TLD, free/disposable detection

      - **Postal Code**: ZIP, country, regional components


      **Graceful Validation**: The `valid` component marks data quality without
      stopping processing. Invalid data doesn't break flows - it's marked for
      you to handle as needed. Use `Phone 1 > Valid is true` in rules to filter
      based on data quality.


      **Raw Value Preservation**: Every type preserves the original input as
      `raw` for audit trails, troubleshooting, and integration flexibility.


      **Privacy-Focused Analytics**: Types support aggregation for reporting
      while excluding sensitive information (usernames from emails, line numbers
      from phones).


      ## Quick Reference


      | Type                                         | Primary Use             |
      Key Components                                       | Common Field
      Examples             |

      |----------------------------------------------|-------------------------|------------------------------------------------------|-----------------------------------|

      | **[Phone](#phone-type)**                     | Contact numbers         |
      area, exchange, line, type, is_tollfree              | Phone 1, Mobile
      Phone, Work Phone |

      | **[Email](#email-type)**                     | Contact emails          |
      domain, user, host, is_free, is_disposable           | Email, Work Email,
      Co-Borrower Email |

      | **[String](#string-type)**                   | General text            |
      none                                                 | Comments,
      Description, Custom fields |

      | **[Number](#number-type)**                   | Numeric data            |
      none                                                 | Loan Amount,
      Income, Credit Score |

      | **[Boolean](#boolean-type)**                 | Yes/no values           |
      none                                                 | Opt In, Has
      Insurance, Accept Terms |

      | **[Date](#date-type)**                       | Date values             |
      none                                                 | Application Date,
      Move Date       |

      | **[DOB](#dob-type-date-of-birth)**           | Birth dates (masked)    |
      age, year (processing only)                          | DOB, Co-Borrower
      DOB              |

      | **[State](#state-type)**                     | US states/provinces     |
      name                                                 | State, Property
      State             |

      | **[Postal Code](#postal-code-type)**         | ZIP/postal codes        |
      zip, country_code, fsa, outcode                      | Postal Code,
      Property Postal Code |

      | **[SSN](#ssn-type-social-security-number)**  | Social Security (masked)
      | first_three, middle_two, last_four (processing only) | Social Security
      Number            |

      | **[First Name](#first-name-type)**           | Given names             |
      none                                                 | First Name,
      Co-Borrower First Name |

      | **[Last Name](#last-name-type)**             | Family names            |
      none                                                 | Last Name,
      Co-Borrower Last Name  |

      | **[City](#city-type)**                       | City names              |
      none                                                 | City, Property
      City               |

      | **[Street](#street-type)**                   | Street addresses        |
      number, name                                         | Address 1, Property
      Address       |

      | **[Gender](#gender-type)**                   | Gender values           |
      abbr                                                 | Gender, Applicant
      Gender          |

      | **[Range](#range-type)**                     | Numeric ranges          |
      min, max, mid, average                               | Age Range, Income
      Range           |

      | **[Time](#time-type)**                       | Date/time values        |
      none                                                 | Submission Time,
      Appointment Time |

      | **[URL](#url-type)**                         | Web addresses           |
      protocol, host, path, query                          | Landing Page URL,
      Referrer URL    |

      | **[Credential](#credential-type)**           | Passwords/keys (masked) |
      none                                                 | API Secret,
      Password              |

      | **[IP](#ip-type)**                           | IP addresses            |
      is_ipv4, ipv6_format                                 | IP Address, Source
      IP             |

      | **[TrustedForm URL](#trustedform-url-type)** | TrustedForm certificates
      | certificate_id, type, is_web                         | TrustedForm
      Certificate URL       |


      ## Complete Type Catalog


      ### Contact Types


      #### Phone Type


      Comprehensive phone number parsing and validation for US/Canada numbers
      with real-world verification.


      **Geographic Limitation**: Currently only supports US and Canadian phone
      numbers. International numbers from other countries will be marked as
      invalid (valid: false) and will not parse correctly, even if they have a
      valid format in their home country.


      **Advanced Validation**: The `valid` property indicates more than just
      format correctness - it verifies:

      - The area code is a real, assigned area code

      - The exchange is a real exchange within that area code

      - The area code and exchange legitimately belong together

      - The number follows valid North American Numbering Plan rules


      This means a phone number like "(999) 555-1234" would be invalid even
      though the format is correct, because 999 is not a real area code.


      **What It Does NOT Verify**: The validation does NOT check if:

      - The complete 10-digit number is actually connected/in service

      - The line (last 4 digits) is valid for that area code and exchange

      - The number can receive calls or texts


      A number can pass type validation but still be disconnected or
      undeliverable.


      **Components:**

      - Raw: Unmodified value

      - Area: Area code

      - Exchange: Exchange code

      - Line: Line number

      - Number: Full normalized number

      - Extension: Extension if present

      - Country Code: Two-letter country code

      - Country Calling Code: Numeric country calling code

      - Type: Number type (home/work/mobile)

      - Is Tollfree: Boolean for toll-free numbers


      **Type Hints:** Append `h` (home), `w` (work), `m` (mobile), or `c` (cell)
      to set type


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`


      **Examples:**

      ```

      Valid US Numbers (real area codes and exchanges):

      "(512) 789-1111" → area: "512", exchange: "789", line: "1111"

      "512-789-1111" → "5127891111"

      "512.789.1111" → "5127891111"


      Invalid Despite Good Format (fake area code):

      "(999) 555-1234" → valid: false (999 not a real area code)

      "(123) 456-7890" → valid: false (123 not a real area code)


      With Extensions:

      "5127891111x123" → extension: "123"

      "5127891111 x 43" → extension: "43"


      Toll-Free:

      "8775551212" → is_tollfree: true

      "1-800-555-1212" → is_tollfree: true


      With Type Hints:

      "5127891111m" → type: "mobile"

      "5127891111h" → type: "home"


      Masked Numbers:

      "1-(512) *** ****" → valid: true (masked)

      "**********" → valid: true (fully masked)


      International (will NOT parse correctly):

      "+49 30 22610" → valid: false (not US/Canadian format)

      "+44 1484 519892" → valid: false (not US/Canadian format)

      Note: International numbers are not supported unless they coincidentally
      match US/Canadian patterns

      ```


      **Invalid inputs** (valid=false): 

      - `"donkey kong"` (not a number)

      - `"964523331"` (too short)

      - `"(999) 555-1234"` (invalid area code)

      - `"(512) 999-1111"` (invalid exchange for 512 area code)


      **Field Examples:** Phone 1, Phone 2, Mobile Phone, Work Phone


      **Common Component Usage:**

      - Check for specific area codes: `Phone 1 > Area equals "512"`

      - Identify toll-free numbers: `Phone 1 > Is Tollfree is true`

      - Format for display: `{{Phone 1 > Area}}-{{Phone 1 > Exchange}}-{{Phone 1
      > Line}}`

      - Check validity: `Phone 1 > Valid is true`


      **Important Limitation**: The `valid` property only confirms the area code
      and exchange are legitimate - it does NOT verify the full number is
      connected or deliverable. The "line" portion (last 4 digits) could still
      be invalid or disconnected. For true deliverability verification, use
      third-party Add-On services like Trestle, Telesign, or BriteVerify in a
      flow step to collect carrier-level metadata, then evaluate the appended
      data in a subsequent filter step to block disconnected numbers.


      ---


      #### Email Type


      Comprehensive email parsing with domain analysis and validation.


      **Components:**

      - Raw: Unmodified value

      - User: Username (before @)

      - Domain: Full domain (after @)

      - Host: Domain without TLD

      - TLD: Top-level domain

      - Is Free: Boolean for free email providers

      - Is Disposable: Boolean for temporary emails


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`

      - `matches pattern` / `does not match pattern`


      **Examples:**

      ```

      Standard:

      "user@domain.com" → user: "user", domain: "domain.com"

      "John.Doe@Gmail.com" → "john.doe@gmail.com" (auto-lowercased)


      Plus Addressing:

      "user+tag@domain.com" → user: "user+tag"

      "test+hola@domain.com" → valid: true


      With Spaces:

      "  user@domain.com  " → "user@domain.com" (trimmed)

      "p. j. butter@gmail.com" → valid: true


      Free Email Detection:

      "user@gmail.com" → is_free: true

      "user@yahoo.com" → is_free: true

      "user@company.com" → is_free: false


      Disposable Detection:

      "user@mailinator.com" → is_disposable: true

      "user@10minutemail.com" → is_disposable: true

      ```


      **Invalid inputs** (valid=false): `"Asdf"` (no @),
      `"hello@//gmail.xn--om.3ia"`


      **Field Examples:** Email, Work Email, Co-Borrower Email


      **Common Component Usage:**

      - Check if email is from a specific domain: `Email > Domain equals
      "company.com"`

      - Identify free email providers: `Email > Is Free is true`

      - Extract username for display: `{{Email > User}}`


      ---


      ### Personal Data Types


      #### First Name Type


      Simple string type for given names with basic validation.


      **Components:**

      - Raw: Unmodified value


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `format is valid` / `format is invalid`

      - `matches pattern` / `does not match pattern`


      **Examples:**

      ```

      "John" → "John"

      "Mary-Jane" → "Mary-Jane"

      "José" → "José"

      "  Alice  " → "Alice"  // Whitespace trimmed

      ```


      **Field Examples:** First Name, Co-Borrower First Name


      ---


      #### Last Name Type


      Simple string type for family names with basic validation.


      **Components:**

      - Raw: Unmodified value


      **Operators:** Same as First Name type


      **Examples:**

      ```

      "Smith" → "Smith"

      "O'Brien" → "O'Brien"

      "García-López" → "García-López"

      ```


      **Field Examples:** Last Name, Co-Borrower Last Name


      ---


      #### DOB Type (Date of Birth)


      Special date type that adds age calculation and is maskable for privacy.


      **Components:**

      - Raw: The full date (only accessible during processing)

      - Age: The age as of today (only accessible during processing)

      - Year: The year from the DOB (only accessible during processing)


      **Maskable:** Yes


      **Operators:** Same as Date type


      **Examples:**

      ```

      During processing:

      "1990-01-15" → {
        raw: "1990-01-15",
        age: 34.9,
        year: 1990,
        valid: true
      }


      After processing (stored as): "****-**-**"

      No components accessible - all values masked

      ```


      **Field Examples:** DOB, Mortgage Co-Borrower DOB, Spouse DOB


      ---


      #### Gender Type


      Parses gender values with abbreviation support.


      **Valid Inputs:**

      - Male: `m`, `male`, `M`, `Male`, `MALE` (case insensitive)

      - Female: `f`, `female`, `F`, `Female`, `FEMALE` (case insensitive)

      - Other: `o`, `other`, `O`, `Other`, `OTHER` (case insensitive)

      - With whitespace: `"  m  "` → male


      **Components:**

      - Raw: Unmodified value

      - Abbreviation: Abbreviated form (M, F, O)


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`


      **Examples:**

      ```

      "male" → {               "f" → {
        normal: "male",           normal: "female",
        raw: "male",              raw: "f",
        abbr: "M",                abbr: "F",
        valid: true               valid: true
      }                         }


      "MALE" → "male"         "other" → abbr: "O"

      "  m  " → "male"        "O" → "other"

      ```


      **Invalid inputs** (valid=false): `"50"`, `"asdf"`, `5`, `true`


      **Field Examples:** Gender, Applicant Gender


      ---


      #### SSN Type (Social Security Number)


      US Social Security Number parsing with masking for privacy.


      **Components:**

      - Raw: Unmodified value (only accessible during processing)

      - First Three: First three digits (only accessible during processing)

      - Middle Two: Middle two digits (only accessible during processing)

      - Last Four: Last four digits (only accessible during processing)


      **Maskable:** Yes


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`


      **Examples:**

      ```

      During processing:

      "123456789" → first_three: "123", middle_two: "45", last_four: "6789"

      "123-45-6789" → "123456789" (dashes removed)

      "123 45 6789" → "123456789" (spaces removed)

      "123.45.6789" → "123456789" (dots removed)

      "  123-45-6789  " → "123456789" (trimmed)

      "donkey 123456789" → "123456789" (text ignored)


      After processing (stored as): "*********"

      No components accessible - all values masked

      ```


      **Invalid inputs** (valid=false): `"abcd"`, `""`, `"   "`


      **Field Examples:** Social Security Number


      **Common Component Usage (During Processing Only):**

      - Check last four digits: `Social Security Number > Last Four equals
      "1234"`

      - Send to integration: `{{Social Security Number > Last Four}}`

      - Check if provided: `Social Security Number > Valid is true`


      **Note:** After processing completes, these components are NOT available


      ---


      ### Location Types


      #### Street Type


      Street address parsing with number and name extraction.


      **Components:**

      - Raw: Unmodified value

      - Number: Street number

      - Name: Street name


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`

      - `matches pattern` / `does not match pattern`


      **Examples:**

      ```

      "123 Main Street" → number: "123", name: "Main Street"

      "4203 Guadalupe St" → number: "4203", name: "Guadalupe St"

      "  123 Main St  " → "123 Main St" (trimmed)

      ```


      **Invalid inputs** (valid=false): `"Main Street"` (no number), `"Asdf"`


      **Field Examples:** Address 1, Property Address


      ---


      #### City Type


      Simple string type for city names with no format validation.


      **Components:**

      - Raw: Unmodified value


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`

      - `matches pattern` / `does not match pattern`


      **Examples:**

      ```

      "Austin" → "Austin"

      "Dallas" → "Dallas"

      "San Francisco" → "San Francisco"

      "  Chicago  " → "Chicago"  // Whitespace trimmed

      ```


      **Field Examples:** City, Property City, Employer City


      ---


      #### State Type


      US states and Canadian provinces with abbreviation normalization.


      **Components:**

      - Raw: Unmodified value

      - Name: Full state/province name


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `includes` / `does not include`

      - `is included in` / `is not included in`


      **Examples:**

      ```

      US States:

      "TX" → name: "Texas"

      "tx" → "TX" (auto-uppercased)

      "Texas" → "TX"

      "texas" → "TX"


      Canadian Provinces:

      "ON" → name: "Ontario"

      "Quebec" → "QC", name: "Quebec"


      Unknown Values (still valid=true):

      "DX" → "DX" (unknown but preserved)

      "Dexus" → "Dexus"

      "DEXUS NEXUS" → "DEXUS NEXUS"

      ```


      **Field Examples:** State, Property State, License State


      ---


      #### Postal Code Type


      Multi-country postal code parsing supporting US, Canada, and UK formats.


      **Components:**

      - Raw: Unmodified value

      - Country Code: Country abbreviation

      - **US Components:**
        - ZIP: First 5 digits
        - Four: Last 4 digits (ZIP+4)
      - **Canadian Components:**
        - FSA: Forward sortation area
        - LDU: Local delivery unit
      - **UK Components:**
        - Outcode: Outward code
        - Incode: Inward code

      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`

      - `matches pattern` / `does not match pattern`


      **Examples:**

      ```

      US Formats:

      "78704" → zip: "78704"

      "78704-1234" → zip: "78704", four: "1234"

      "78704 1234" → "78704-1234"

      "787041234" → "78704-1234" (9 digits)

      "78704-" → "78704" (trailing dash removed)

      "78704   - 1234" → "78704-1234" (extra spaces)


      Canadian Formats:

      "Q2E 4U7" → fsa: "Q2E", ldu: "4U7"

      "q2e4u7" → "Q2E 4U7" (auto-formatted)

      "q2e      4u7" → "Q2E 4U7" (extra spaces)


      UK Formats (5 patterns):

      "A1 1AA" → outcode: "A1", incode: "1AA"

      "A11AA" → "A1 1AA" (space added)

      "AA11 1AA" → valid UK format

      "AA11A 1AA" → valid UK format

      ```


      **Invalid inputs** (valid=false): `"garbage"`, non-postal code strings


      **Field Examples:** Postal Code, Property Postal Code


      **Common Component Usage:**

      - Check ZIP code: `Postal Code > ZIP equals "78751"`

      - Identify country: `Postal Code > Country Code equals "US"`

      - Use only 5-digit ZIP for matching: `{{Postal Code > ZIP}}`


      ---


      ### Data Types


      #### Boolean Type


      Parses boolean values from various string and numeric representations.


      **Valid Inputs:**

      - True: `y`, `yes`, `true`, `t`, `1` (case insensitive)

      - False: `n`, `no`, `false`, `f`, `0` (case insensitive)

      - Numeric: `1` → true, `0` → false

      - With whitespace: `"  true  "` → true


      **Components:** None


      **Operators:**

      - `is true` / `is not true`

      - `is false` / `is not false`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`


      **Examples:**

      ```

      "yes" → true        "no" → false

      "Y" → true          "N" → false  

      "TRUE" → true       "FALSE" → false

      "t" → true          "f" → false

      1 → true             0 → false

      "1" → true          "0" → false

      ```


      **Invalid inputs** (return false with valid=false): `"50"`, `"asdf"`,
      `{foo: 42}`


      **Field Examples:** Opt In, Accept Prepayment Penalty, Has Insurance


      ---


      ### City Type


      Simple string type for city names with no format validation.


      **Components:**

      - Raw: Unmodified value


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`

      - `matches pattern` / `does not match pattern`


      **Examples:**

      ```

      "Austin" → "Austin"

      "Dallas" → "Dallas"

      "San Francisco" → "San Francisco"

      "  Chicago  " → "Chicago"  // Whitespace trimmed

      ```


      **Field Examples:** City, Property City, Employer City


      ---


      ### Credential Type


      Handles sensitive credential strings like passwords and API keys. Always
      masked by default.


      **Components:** None


      **Maskable:** Yes (sensitive data replaced with asterisks)


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `includes` / `does not include`

      - `is included in` / `is not included in`


      **Examples:**

      ```

      During processing: "sekret-pazzward" → available for integrations

      After processing (stored as): "****************"


      During processing: "befa4e7379d81173dfe8d1a53deaf591" → available for
      integrations  

      After processing (stored as): "********************************"

      ```


      **Field Examples:** API Secret, Password, Authentication Token


      ---


      ### Date Type


      Parses dates in multiple formats and normalizes to ISO format
      (YYYY-MM-DD).


      **Supported Formats:**

      - US: `M/D/YYYY` (`6/2/2014`), `M/D/YY` (`6/2/14`), `MM-DD-YYYY`
      (`06-02-2014`)

      - ISO: `YYYY-MM-DD` (`2014-06-02`)

      - European: `D/M/YYYY` (`18/7/2014`), `D-M-YYYY` (`18-7-2014`)

      - Compact: `MMDDYYYY` (`06022014`), `YYYYMMDD` (`20140602`)

      - Natural: `Mon Jun 02 2014`, `Jun 02 2014`, `Fri 18 July 2014`


      **Components:**

      - Raw: Unmodified value


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is less than` / `is less than or equal to`

      - `is greater than` / `is greater than or equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `is between` / `is not between`


      **Examples:**

      ```

      "6/2/2014" → "2014-06-02"      "06/02/2014" → "2014-06-02"

      "6/2/14" → "2014-06-02"        "June 2, 2014" → "2014-06-02"

      "2014-06-02" → "2014-06-02"    "Mon Jun 02 2014" → "2014-06-02"

      "06-02-2014" → "2014-06-02"    "06022014" → "2014-06-02"

      "20140602" → "2014-06-02"      "18/07/2014" → "2014-07-18" (European)

      ```


      **Field Examples:** Application Date, Move Date, Purchase Date


      ---


      ### DOB Type (Date of Birth)


      Special date type that adds age calculation and is maskable for privacy.


      **Components:**

      - Raw: The full date (only accessible during processing)

      - Age: The age as of today (only accessible during processing)

      - Year: The year from the DOB (only accessible during processing)


      **Maskable:** Yes


      **Operators:** Same as Date type


      **Examples:**

      ```

      During processing:

      "1990-01-15" → {
        raw: "1990-01-15",
        age: 34.9,
        year: 1990,
        valid: true
      }


      After processing (stored as): "****-**-**"

      No components accessible - all values masked

      ```


      **Field Examples:** DOB, Mortgage Co-Borrower DOB, Spouse DOB


      ---


      ### Email Type


      Comprehensive email parsing with domain analysis and validation.


      **Components:**

      - Raw: Unmodified value

      - User: Username (before @)

      - Domain: Full domain (after @)

      - Host: Domain without TLD

      - TLD: Top-level domain

      - Is Free: Boolean for free email providers

      - Is Disposable: Boolean for temporary emails


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`

      - `matches pattern` / `does not match pattern`


      **Examples:**

      ```

      Standard:

      "user@domain.com" → user: "user", domain: "domain.com"

      "John.Doe@Gmail.com" → "john.doe@gmail.com" (auto-lowercased)


      Plus Addressing:

      "user+tag@domain.com" → user: "user+tag"

      "test+hola@domain.com" → valid: true


      With Spaces:

      "  user@domain.com  " → "user@domain.com" (trimmed)

      "p. j. butter@gmail.com" → valid: true


      Free Email Detection:

      "user@gmail.com" → is_free: true

      "user@yahoo.com" → is_free: true

      "user@company.com" → is_free: false


      Disposable Detection:

      "user@mailinator.com" → is_disposable: true

      "user@10minutemail.com" → is_disposable: true

      ```


      **Invalid inputs** (valid=false): `"Asdf"` (no @),
      `"hello@//gmail.xn--om.3ia"`


      **Field Examples:** Email, Work Email, Co-Borrower Email


      **Common Component Usage:**

      - Check if email is from a specific domain: `Email > Domain equals
      "company.com"`

      - Identify free email providers: `Email > Is Free is true`

      - Extract username for display: `{{Email > User}}`


      ---


      ### First Name Type


      Simple string type for given names with basic validation.


      **Components:**

      - Raw: Unmodified value


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `format is valid` / `format is invalid`

      - `matches pattern` / `does not match pattern`


      **Examples:**

      ```

      "John" → "John"

      "Mary-Jane" → "Mary-Jane"

      "José" → "José"

      "  Alice  " → "Alice"  // Whitespace trimmed

      ```


      **Field Examples:** First Name, Co-Borrower First Name


      ---


      ### Gender Type


      Parses gender values with abbreviation support.


      **Valid Inputs:**

      - Male: `m`, `male`, `M`, `Male`, `MALE` (case insensitive)

      - Female: `f`, `female`, `F`, `Female`, `FEMALE` (case insensitive)

      - Other: `o`, `other`, `O`, `Other`, `OTHER` (case insensitive)

      - With whitespace: `"  m  "` → male


      **Components:**

      - Raw: Unmodified value

      - Abbreviation: Abbreviated form (M, F, O)


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`


      **Examples:**

      ```

      "male" → {               "f" → {
        normal: "male",           normal: "female",
        raw: "male",              raw: "f",
        abbr: "M",                abbr: "F",
        valid: true               valid: true
      }                         }


      "MALE" → "male"         "other" → abbr: "O"

      "  m  " → "male"        "O" → "other"

      ```


      **Invalid inputs** (valid=false): `"50"`, `"asdf"`, `5`, `true`


      **Field Examples:** Gender, Applicant Gender


      ---


      ### IP Type


      Handles both IPv4 and IPv6 addresses with format detection.


      **Components:**

      - Raw: Unmodified value

      - Is IPv4: Boolean indicating IPv4 format

      - IPv6 Format: IP converted to IPv6 format


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`


      **Examples:**

      ```

      IPv4:

      "198.51.90.161" → is_ipv4: true, ipv6_format: "::ffff:198.51.90.161"

      "198.51.90.161/32" → CIDR notation supported

      "http://198.51.90.161:32" → extracted from URL

      "    198. 51. 90. 161     " → "198.51.90.161" (spaces removed)


      IPv6:

      "8faa:230d:52ab:98f3:a2ea:7735:a7b8:72e9" → is_ipv4: false

      "[8faa:230d:52ab:98f3:a2ea:7735:a7b8:72e9]" → brackets removed

      "8faa:230d:52ab:98f3:a2ea:7735:a7b8:72e9/64" → CIDR notation

      "[0000:0000:0000:0000:0000:0000:0000:0001]" → "::1" (compressed)

      "[ffff::186.25.192.233]" → "ffff::ba19:c0e9" (IPv4 tail)

      ```


      **Invalid inputs** (valid=false): `"256.4.5.6"`, `"fe80:2030:31:24"`
      (incomplete IPv6)


      **Field Examples:** IP Address, Source IP


      ---


      ### Last Name Type


      Simple string type for family names with basic validation.


      **Components:**

      - Raw: Unmodified value


      **Operators:** Same as First Name type


      **Examples:**

      ```

      "Smith" → "Smith"

      "O'Brien" → "O'Brien"

      "García-López" → "García-López"

      ```


      **Field Examples:** Last Name, Co-Borrower Last Name


      ---


      ### Number Type


      Parses numeric values from strings, handling currency symbols and
      thousands separators.


      **Components:** None


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is less than` / `is less than or equal to`

      - `is greater than` / `is greater than or equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`

      - `is between` / `is not between`


      **Examples:**

      ```

      "100" → 100               "$1,000.99" → 1000.99

      100 → 100                  "1,234,567" → 1234567

      "1.111" → 1.111           "-11" → -11

      "$1100 per month" → 1100  "-$1,100" → -1100

      "  1100  " → 1100         "1,100" → 1100

      ```


      **Invalid inputs** (valid=false): `true`, `[1]`, `{foo: 'bar'}`, `"derp"`,
      `NaN`


      **Field Examples:** Loan Amount, Monthly Income, Credit Score


      ---


      ### Phone Type


      Comprehensive phone number parsing and validation for US/Canada numbers
      with real-world verification.


      **Geographic Limitation**: Currently only supports US and Canadian phone
      numbers. International numbers from other countries will be marked as
      invalid (valid: false) and will not parse correctly, even if they have a
      valid format in their home country.


      **Advanced Validation**: The `valid` property indicates more than just
      format correctness - it verifies:

      - The area code is a real, assigned area code

      - The exchange is a real exchange within that area code

      - The area code and exchange legitimately belong together

      - The number follows valid North American Numbering Plan rules


      This means a phone number like "(999) 555-1234" would be invalid even
      though the format is correct, because 999 is not a real area code.


      **What It Does NOT Verify**: The validation does NOT check if:

      - The complete 10-digit number is actually connected/in service

      - The line (last 4 digits) is valid for that area code and exchange

      - The number can receive calls or texts


      A number can pass type validation but still be disconnected or
      undeliverable.


      **Components:**

      - Raw: Unmodified value

      - Area: Area code

      - Exchange: Exchange code

      - Line: Line number

      - Number: Full normalized number

      - Extension: Extension if present

      - Country Code: Two-letter country code

      - Country Calling Code: Numeric country calling code

      - Type: Number type (home/work/mobile)

      - Is Tollfree: Boolean for toll-free numbers


      **Type Hints:** Append `h` (home), `w` (work), `m` (mobile), or `c` (cell)
      to set type


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`


      **Examples:**

      ```

      Valid US Numbers (real area codes and exchanges):

      "(512) 789-1111" → area: "512", exchange: "789", line: "1111"

      "512-789-1111" → "5127891111"

      "512.789.1111" → "5127891111"


      Invalid Despite Good Format (fake area code):

      "(999) 555-1234" → valid: false (999 not a real area code)

      "(123) 456-7890" → valid: false (123 not a real area code)


      With Extensions:

      "5127891111x123" → extension: "123"

      "5127891111 x 43" → extension: "43"


      Toll-Free:

      "8775551212" → is_tollfree: true

      "1-800-555-1212" → is_tollfree: true


      With Type Hints:

      "5127891111m" → type: "mobile"

      "5127891111h" → type: "home"


      Masked Numbers:

      "1-(512) *** ****" → valid: true (masked)

      "**********" → valid: true (fully masked)


      International (will NOT parse correctly):

      "+49 30 22610" → valid: false (not US/Canadian format)

      "+44 1484 519892" → valid: false (not US/Canadian format)

      Note: International numbers are not supported unless they coincidentally
      match US/Canadian patterns

      ```


      **Invalid inputs** (valid=false): 

      - `"donkey kong"` (not a number)

      - `"964523331"` (too short)

      - `"(999) 555-1234"` (invalid area code)

      - `"(512) 999-1111"` (invalid exchange for 512 area code)


      **Field Examples:** Phone 1, Phone 2, Mobile Phone, Work Phone


      **Common Component Usage:**

      - Check for specific area codes: `Phone 1 > Area equals "512"`

      - Identify toll-free numbers: `Phone 1 > Is Tollfree is true`

      - Format for display: `{{Phone 1 > Area}}-{{Phone 1 > Exchange}}-{{Phone 1
      > Line}}`

      - Check validity: `Phone 1 > Valid is true`


      **Important Limitation**: The `valid` property only confirms the area code
      and exchange are legitimate - it does NOT verify the full number is
      connected or deliverable. The "line" portion (last 4 digits) could still
      be invalid or disconnected. For true deliverability verification, use
      third-party Add-On services like Trestle, Telesign, or BriteVerify in a
      flow step to collect carrier-level metadata, then evaluate the appended
      data in a subsequent filter step to block disconnected numbers.


      ---


      ### Postal Code Type


      Multi-country postal code parsing supporting US, Canada, and UK formats.


      **Components:**

      - Raw: Unmodified value

      - Country Code: Country abbreviation

      - **US Components:**
        - ZIP: First 5 digits
        - Four: Last 4 digits (ZIP+4)
      - **Canadian Components:**
        - FSA: Forward sortation area
        - LDU: Local delivery unit
      - **UK Components:**
        - Outcode: Outward code
        - Incode: Inward code

      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`

      - `matches pattern` / `does not match pattern`


      **Examples:**

      ```

      US Formats:

      "78704" → zip: "78704"

      "78704-1234" → zip: "78704", four: "1234"

      "78704 1234" → "78704-1234"

      "787041234" → "78704-1234" (9 digits)

      "78704-" → "78704" (trailing dash removed)

      "78704   - 1234" → "78704-1234" (extra spaces)


      Canadian Formats:

      "Q2E 4U7" → fsa: "Q2E", ldu: "4U7"

      "q2e4u7" → "Q2E 4U7" (auto-formatted)

      "q2e      4u7" → "Q2E 4U7" (extra spaces)


      UK Formats (5 patterns):

      "A1 1AA" → outcode: "A1", incode: "1AA"

      "A11AA" → "A1 1AA" (space added)

      "AA11 1AA" → valid UK format

      "AA11A 1AA" → valid UK format

      ```


      **Invalid inputs** (valid=false): `"garbage"`, non-postal code strings


      **Field Examples:** Postal Code, Property Postal Code


      **Common Component Usage:**

      - Check ZIP code: `Postal Code > ZIP equals "78751"`

      - Identify country: `Postal Code > Country Code equals "US"`

      - Use only 5-digit ZIP for matching: `{{Postal Code > ZIP}}`


      ---


      ### Range Type


      Parses numeric ranges and single numbers, calculating statistical values.


      **Components:**

      - Raw: Unmodified value

      - Min: Lowest number in range

      - Max: Highest number in range

      - Mid: Average rounded down to nearest whole number

      - Average: Average of min and max


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is less than` / `is less than or equal to`

      - `is greater than` / `is greater than or equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`


      **Examples:**

      ```

      Range Formats:

      "100 to 200" → min: 100, max: 200, mid: 150

      "100 - 200" → min: 100, max: 200, mid: 150

      "100 200" → min: 100, max: 200  (space only)


      Greater Than:

      "50+" → min: 50, max: Infinity

      "10+" → min: 10, max: null


      Single Values:

      "100" → min: 100, max: 100, mid: 100

      100 → min: 100, max: 100


      Decimals:

      "5.5" → min: 5.5, max: 5.5

      "999.95 to 10000.95" → min: 999.95, max: 10000.95


      Currency:

      "$1 to $10" → min: 1, max: 10

      "$1,000 to $10,000" → min: 1000, max: 10000


      Special Cases:

      "787041234" → "78704-1234" (9 digits treated as ZIP+4)

      "-10" → min: -10, max: -10

      "-10+" → min: -10, max: null

      "  1 to 10  " → min: 1, max: 10 (whitespace trimmed)


      Date/Time Ranges:

      "2015-07-01 - 2015-07-25" → date range

      "2015-07-01T01:59:32.022Z - 2015-07-25T01:59:32:021Z" → time range

      ```


      **Invalid inputs** (valid=false): `"asdf"`, non-numeric strings


      **Field Examples:** Age, Income Range, Years at Residence


      ---


      ### SSN Type (Social Security Number)


      US Social Security Number parsing with masking for privacy.


      **Components:**

      - Raw: Unmodified value (only accessible during processing)

      - First Three: First three digits (only accessible during processing)

      - Middle Two: Middle two digits (only accessible during processing)

      - Last Four: Last four digits (only accessible during processing)


      **Maskable:** Yes


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`


      **Examples:**

      ```

      During processing:

      "123456789" → first_three: "123", middle_two: "45", last_four: "6789"

      "123-45-6789" → "123456789" (dashes removed)

      "123 45 6789" → "123456789" (spaces removed)

      "123.45.6789" → "123456789" (dots removed)

      "  123-45-6789  " → "123456789" (trimmed)

      "donkey 123456789" → "123456789" (text ignored)


      After processing (stored as): "*********"

      No components accessible - all values masked

      ```


      **Invalid inputs** (valid=false): `"abcd"`, `""`, `"   "`


      **Field Examples:** Social Security Number


      **Common Component Usage (During Processing Only):**

      - Check last four digits: `Social Security Number > Last Four equals
      "1234"`

      - Send to integration: `{{Social Security Number > Last Four}}`

      - Check if provided: `Social Security Number > Valid is true`


      **Note:** After processing completes, these components are NOT available


      ---


      ### State Type


      US states and Canadian provinces with abbreviation normalization.


      **Components:**

      - Raw: Unmodified value

      - Name: Full state/province name


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `includes` / `does not include`

      - `is included in` / `is not included in`


      **Examples:**

      ```

      US States:

      "TX" → name: "Texas"

      "tx" → "TX" (auto-uppercased)

      "Texas" → "TX"

      "texas" → "TX"


      Canadian Provinces:

      "ON" → name: "Ontario"

      "Quebec" → "QC", name: "Quebec"


      Unknown Values (still valid=true):

      "DX" → "DX" (unknown but preserved)

      "Dexus" → "Dexus"

      "DEXUS NEXUS" → "DEXUS NEXUS"

      ```


      **Field Examples:** State, Property State, License State


      ---


      ### Street Type


      Street address parsing with number and name extraction.


      **Components:**

      - Raw: Unmodified value

      - Number: Street number

      - Name: Street name


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`

      - `matches pattern` / `does not match pattern`


      **Examples:**

      ```

      "123 Main Street" → number: "123", name: "Main Street"

      "4203 Guadalupe St" → number: "4203", name: "Guadalupe St"

      "  123 Main St  " → "123 Main St" (trimmed)

      ```


      **Invalid inputs** (valid=false): `"Main Street"` (no number), `"Asdf"`


      **Field Examples:** Address 1, Property Address


      ---


      ### String Type


      Universal text type with no format requirements.


      **Components:** None


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `includes` / `does not include`

      - `is included in` / `is not included in`

      - `matches pattern` / `does not match pattern`


      **Examples:** Any text value


      **Field Examples:** Comments, Description, Custom Field 1


      ---


      ### Time Type


      Natural language datetime parsing with flexible format support.


      **Components:**

      - Raw: Unmodified value


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is less than` / `is less than or equal to`

      - `is greater than` / `is greater than or equal to`

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`

      - `is between` / `is not between`


      **Examples:**

      ```

      Natural Language:

      "June 14, 2015 6:27 PM" → "2015-06-14T18:27:00.000Z"

      "Sat Jun 14 2014 13:27:33 GMT-0500 (CDT)" → ISO datetime

      "yesterday at 3pm" → (calculated datetime)


      ISO Formats:

      "2015-06-14T18:27:33Z" → "2015-06-14T18:27:33.000Z"

      "2015-06-14T18:27:33.123Z" → with milliseconds

      "2015-06-14T18:27:33.12345Z" → with microseconds


      Other Formats:

      "06/14/2014 6:27:33 PM" → ISO datetime

      JavaScript Date objects → ISO datetime

      ```


      **Invalid inputs** (valid=false): `"garbage"`, `{foo: 42}`


      **Field Examples:** Submission Time, Appointment Time


      ---


      ### TrustedForm URL Type


      Specialized type for TrustedForm certificate URLs with validation.


      **Components:**

      - Raw: Unmodified certificate URL

      - Certificate ID: Certificate identifier

      - Type: Certificate type (web/facebook/mobile/masked)

      - Is Masked: Boolean for masked certificates

      - Is Web: Boolean for web certificates

      - Is Mobile: Boolean for mobile certificates

      - Is Facebook: Boolean for Facebook Lead Ads certificates


      **Special Validation:**

      - Must be HTTPS

      - Must be from valid TrustedForm domain

      - Certificate expires after 90 days

      - Environment must match (production/staging)


      **Operators:**

      - `is blank` / `is not blank`

      - `format is valid` / `format is invalid`


      **Examples:**

      ```

      Web Certificates:

      "https://cert.trustedform.com/eb9fc4dd9bed9ad451a5648946cf4bf09b5bb947"
        → type: "web", is_web: true

      Facebook Certificates:

      "https://cert.trustedform.com/0.GUr7[...very long...]" (with dots)
        → type: "facebook", is_facebook: true

      Mobile Certificates:

      "https://cert.trustedform.com/[64-character-id]"
        → type: "mobile", is_mobile: true

      Staging/Dev URLs:

      Allowed in non-production environments

      ```


      **Invalid inputs** (valid=false): 

      - Non-HTTPS URLs

      - Wrong domain

      - Malformed certificate IDs

      - Expired certificates (>90 days)


      **Field Examples:** TrustedForm Certificate URL


      ---


      ### URL Type


      General URL parsing with component extraction.


      **Components:**

      - Raw: Unmodified value

      - Protocol: Protocol (http/https)

      - Host: Host name

      - Port: Port number if present

      - Path: URL path

      - Query: Query string

      - Hash: Fragment/hash if present


      **Operators:**

      - `is equal to` / `is not equal to`

      - `is blank` / `is not blank`

      - `is obscene` / `is not obscene`

      - `format is valid` / `format is invalid`

      - `includes` / `does not include`

      - `is included in` / `is not included in`


      **Examples:**

      ```

      Full URLs:

      "https://google.com/search?q=hi#results"
        → protocol: "https", host: "google.com", path: "/search", query: "q=hi", hash: "results"

      Partial URLs:

      "centennialbulb.org" → host: "centennialbulb.org" (no protocol)

      "172.0.0.1" → valid IP as URL

      "http" → protocol only

      "whatever" → treated as domain

      ```


      **Invalid inputs** (valid=false): `""`, `" "`, `"https://"`,
      `"donkey://google.com"`


      **Field Examples:** Landing Page URL, Referrer URL


      ## How Types Work in Practice


      ### Automatic Normalization - No Manual Work Needed


      Types automatically normalize data - you don't need to:

      - **Email**: Automatically lowercased (never use `{{lowercase email}}`)

      - **Phone**: Automatically normalized to 10 digits

      - **State**: Automatically converted to 2-letter abbreviation

      - **Postal Code**: Automatically formatted

      - **Names**: Automatically trimmed of whitespace


      The type system handles ALL normalization - just pass the raw data!


      ### Standardization in Action


      Types accept chaotic input and produce consistent output:


      **Phone Number Standardization:**

      ```

      "(512) 789-1111" → "5127891111"

      "512-789-1111"   → "5127891111"  

      "512.789.1111"   → "5127891111"

      "5127891111"     → "5127891111"

      "+1 512 789 1111" → "5127891111"

      ```


      All produce the same normalized format with identical components.


      **Date Standardization:**

      ```

      "6/2/2014"      → "2014-06-02"

      "June 2, 2014"  → "2014-06-02"

      "2014-06-02"    → "2014-06-02"

      "06-02-2014"    → "2014-06-02"

      "20140602"      → "2014-06-02"

      ```


      All produce ISO format dates.


      ### Validation Without Rejection


      When types encounter invalid data:


      1. **Parsing continues**: The system doesn't stop

      2. **`valid` is set to false**: Marking the data quality issue

      3. **`raw` preserves input**: Original data is never lost

      4. **Components may be empty**: But the structure remains

      5. **Flows continue**: You decide how to handle invalid data


      Example:

      ```javascript

      // Invalid phone number

      Input: "not a phone"

      Result: {
        normal: "notaphone",  // Non-digits removed
        raw: "not a phone",   // Original preserved
        valid: false,         // Marked as invalid
        // No components extracted
      }

      // Lead continues processing!

      ```


      ### Component Usage Best Practices


      **Always prefer components over string manipulation:**


      ✅ **Good - Using Components:**

      ```javascript

      // Check area code

      Phone 1 > Area equals "512"


      // Check email domain

      Email > Domain equals "gmail.com"


      // Check ZIP code

      Postal Code > ZIP equals "78701"

      ```


      ❌ **Bad - String Manipulation:**

      ```javascript

      // Don't do this!

      substring(Phone 1, 0, 3) equals "512"

      split(Email, "@")[1] equals "gmail.com"

      substring(Postal Code, 0, 5) equals "78701"

      ```


      Components are:

      - Already parsed and validated

      - More efficient (no runtime parsing)

      - More reliable (handle edge cases)

      - Clearer in intent


      ## Special Type Behaviors


      ### Maskable Types


      Some types contain sensitive data that are automatically masked for
      security:

      - **SSN**: Social Security Numbers - all components masked

      - **DOB**: Dates of birth - all components masked (including age and year)

      - **Credential**: Passwords and API keys - entire value masked


      **How Masking Works:**


      1. **During Processing**: LeadConduit can use the full, unmasked field
      value and all components while the lead is being processed

      2. **After Processing**: Values are written to the database as `*`
      characters only

      3. **No Components Preserved**: Unlike other types, maskable types do NOT
      preserve any components - everything is masked

      4. **Permanent**: The unmasked values and components exist only in memory
      during processing - they are gone forever after that

      5. **What You See**: 
         - UI displays: Masked values only, no components
         - API responses: Masked values only, no components
         - Event exports: Masked values only, no components
         - Reports: No data available for these fields

      **Example of SSN Masking:**

      ```

      During processing: "123-45-6789" → Available for rules and integrations

      In database/UI/API: "*********" → Only masked version stored

      Components: NOT preserved - no SSN data accessible after processing

      ```


      **Example of DOB Masking:**

      ```

      During processing: "1990-01-15" → Age: 34.9, Year: 1990 available

      In database/UI/API: "****-**-**" → Only masked version stored

      Components: NOT preserved - no DOB data accessible after processing

      ```


      **Example of Credential Masking:**

      ```

      During processing: "mypassword123" → Available for integrations

      In database/UI/API: "*************" → Only masked version stored

      ```


      This ensures sensitive data can be used for processing (validation,
      delivery to buyers) while maintaining security and compliance by never
      storing the actual values or any components.


      ### Types with Special Validation


      **TrustedForm URL**:

      - Must be HTTPS

      - Must be from valid TrustedForm domain

      - Certificate must not be older than 90 days

      - Environment must match (production/staging)


      **Email**:

      - Detects free providers (Gmail, Yahoo, etc.)

      - Identifies disposable/temporary emails

      - Normalizes to lowercase


      **Phone**:

      - Validates actual area codes and exchanges (not just format)

      - Verifies area code/exchange combinations are real

      - Only supports US/Canadian numbers currently

      - Supports type hints (h=home, w=work, m=mobile)

      - Detects toll-free numbers

      - Handles extensions

      - Recognizes masked numbers


      ### Always-Valid Types


      Some types never return `valid: false`:

      - **String**: Any text is valid

      - **City**: Any city name is valid

      - **State**: Any locality is valid

      - **First/Last Name**: Any name is valid

      - **Credential**: Any credential is valid


      These types accept any string input because there's no universal format to
      validate against.


      ## Type Selection Guide


      ### When to Use Each Type


      **String**: Default for text without specific format requirements

      - Custom fields

      - Comments

      - Descriptions

      - Generic text data


      **Number**: Numeric data for calculations or comparisons

      - Prices

      - Quantities  

      - Scores

      - IDs


      **Boolean**: Yes/no decisions

      - Opt-ins

      - Preferences

      - Flags

      - Consent


      **Email**: Contact emails requiring validation

      - Primary contact

      - Marketing emails

      - Account emails


      **Phone**: Phone numbers needing standardization

      - Contact numbers

      - SMS numbers

      - Call tracking


      **Date/Time**: Temporal data

      - Submission dates

      - Appointment times

      - Deadlines


      **DOB**: Special handling for birth dates

      - Age verification

      - Age-based decisions

      - Demographics


      **Postal Code**: Location-based decisions

      - Geographic targeting

      - Shipping addresses

      - Tax determination


      **SSN**: Highly sensitive identification

      - Credit checks

      - Identity verification

      - Compliance requirements


      ## Common Patterns


      ### Multi-Format Input Handling

      Types gracefully handle various input formats:

      ```

      Boolean: "yes", "y", "1", "true" → true

      State: "TX", "Texas", "tx", "texas" → "TX"

      Phone: Multiple formats → Single standard

      ```


      ### Progressive Enhancement

      Types extract as much value as possible:

      ```

      Valid email → All components extracted

      Invalid email → Still preserves raw, domain might be partial

      Valid phone → All components available

      Invalid phone → Raw preserved, some components may exist

      ```


      ### Business Logic Enablement

      Types enable sophisticated rules without complexity:

      ```

      IF Email > Is Free is true THEN apply different pricing

      IF Phone 1 > Is Tollfree is true THEN flag for review

      IF DOB > Age is less than 18 THEN require guardian consent

      IF Postal Code > Country Code equals "US" THEN use US validation

      ```


      ## Integration with LeadConduit


      ### Field Type Assignment

      In flow configuration:

      1. Each field can be assigned a type

      2. Types are applied during lead processing

      3. Parsed data replaces raw input

      4. Components become available immediately


      ### Rule Evaluation

      Types integrate with the rules engine:

      - Use `valid` property in conditions

      - Access components directly

      - Compare normalized values

      - Apply type-specific operators


      ### Data Flow

      1. **Input arrives** in various formats

      2. **Types parse** based on field configuration

      3. **Normalization** prepares for storage

      4. **Components** available in rules/mappings

      5. **Storage** uses normalized format

      6. **Delivery** can use any component

  - name: Rules
    x-traitTag: true
    description: >
      LeadConduit uses a rules engine to customize flow behavior based on field
      data, appended data, or internal

      tracking metadata (such as the submission timestamp). The rules engine is
      built into flow acceptance criteria,

      filtering, volume caps, pricing, mappings, and more. 


      ### Rule Set


      Rules belong to a set which has the following properties:

       * `op` &mdash; `and` or `or` determines whether all rules in set must pass or just one
       * `rules` &mdash; the array of Rules and/or Rule Sets to evaluate

      ### Rules


      Each element of the `rules` array can be a Rule or another Rule Set.


      Rules have the following properties:
       * `lhv` &mdash; left hand value is a key to look up a value from the lead data context
       * `op` &mdash; operator Name of the operator 
       * `rhv` &mdash; right hand value (omit for unary operators like `is blank`)
       * `rule_set` &mdash; optional rule set to be ANDed with the rule

      ### Variables


      When rules are processed the `lhv` will be resolved to a value using the
      data in the variables found under that key. For example, if the `lhv`

      is "lead.first_name" and the variables are `{ "lead": { "first_name":
      "Bob" } }`, then the resolved `lhv` will be "Bob".


      ### Templating


      The `lhv` and `rhv` of each rule supports
      (templating)[https://developers.activeprospect.com/api-reference/leadconduit/templates].
      The variables are used to evaluate the template.
  - name: Variables
    x-traitTag: true
    description: |
      A variable is a key/value pair that is available to filters, rule sets
      and mappings and in templates at run time while processing a lead.

      Every lead is born with a standard set of variables, and additional
      variables are added at run-time as leads are processed by each step in
      the flow. The full set of variables are stored on every event (in the
      `vars` property) generated during lead processing.

      Variables are stored on events as nested objects and are referenced at
      runtime using dot-notation. There are several top-level variable prefixes
      that logically group variables.
  - name: Caps and Limits
    description: >
      LeadConduit supports two controls that set constraints on lead submissions
      and pings. 


      ### Cap

      A cap allows limiting the number of leads sent to a flow. If a lead is
      successful, it will be counted against the

      `maximum` configured leads. If configured, the rule set will be evaluated
      to determine whether a lead will be counted

      against the cap.


      ### Ping Limit

      A ping limit caps how many pings a source can send to a flow within a
      period of time. It is configured on the

      flow, or on a source within the flow, with a `maximum`, a `duration` and
      its `duration_units` (minute, hour, day,

      week, or month), and a `time_zone` that decides when the period resets.
      Pings over the limit are rejected with

      `429 Too Many Requests`, a `retry_after` value in milliseconds and a
      `Retry-After` header. Ping limits only apply to

      `/ping`; leads sent to `/submit` are not counted. The counter for a ping
      limit is kept as a standalone record that

      shares the ping limit's ID.
  - name: Lead Submission
    x-traitTag: true
    description: |+
      How lead handling works

  - name: Templates
    x-traitTag: true
    description: >
      # LeadConduit Templates


      ## Overview


      Templates are the dynamic value engine of LeadConduit. They transform
      static configurations into intelligent, adaptive systems by resolving
      variables, computing values, and manipulating data at runtime. Think of
      templates as smart placeholders that know how to fetch and transform data
      when needed.


      Templates answer a fundamental question:

      **How do I get the right value at the right time?**


      Templates work seamlessly with rules and mappings:

      - **Rules** use templates for dynamic comparisons

      - **Mappings** use templates to compute values

      - **Templates** provide the bridge between configuration and runtime data


      ## Why Templates Exist


      ### The Static Configuration Problem

      Without templates, every value would be hard-coded:

      - Phone format: "(512) 789-1111" for everyone

      - Greeting: "Hello Customer" instead of personalized

      - Dates: Fixed instead of calculated

      - Prices: Static instead of computed


      Templates make configurations come alive with actual data.


      ### The Data Access Problem

      Lead data has complex structures:

      - Nested objects: `address_1`, `city`

      - Array elements: `tags[0]`

      - Type components: `phone.area`

      - Calculated values: `price * tax_rate`


      Templates provide a consistent way to access any data, anywhere.


      ### The Transformation Problem

      Raw data rarely matches what you need:

      - Dates need formatting

      - Names need case changes

      - Numbers need calculations

      - Strings need manipulation


      Templates include helpers that transform data on the fly.


      ### Technical Details


      LeadConduit supports combining,
      [formatting](https://developers.activeprospect.com/api-reference/leadconduit/templates#variable-formatting),

      [hashing](https://developers.activeprospect.com/api-reference/leadconduit/templates#variable-hashing),
      and [performing
      math](https://developers.activeprospect.com/api-reference/leadconduit/templates#variable-math)
      on

      values using template markup. Templating in LeadConduit is based on the

      popular [Handlebars](https://handlebarsjs.com/) semantic templating

      library. A template is a string which contains any number of variable

      placeholders


      ## Template Variable


      Variable placeholders in templates start and end with two curly-brace

      characters: `{{ lead.first_name }}`. Multiple placeholders can be

      combined in a single template: `{{ lead.first_name }} {{ lead.last_name

      }}`. The [universe of possible
      variables](https://developers.activeprospect.com/api-reference/leadconduit/variables)
      available to a

      template depends on the
      [fields](https://developers.activeprospect.com/api-reference/leadconduit/fields/field)
      defined in your flow and the

      steps you've added to your flow.


      ## Variable Formatting


      LeadConduit has a built-in helper for formatting numbers and dates.

      Formatting a value is done with the `format` helper.  If the value is a

      date field, then you may use date formatting options with the helper.

      If it's a number field, then you may use the number formatting options

      with the helper.


      ### Date Variable Formatting


      To format a date, use the `format` helper: `{{ format lead.dob

      format="YYYY-MM-DD" }}` results in '2015-06-24'. The `format` option is

      a string which defines the format of the date. This format can be any

      combination of the below tokens.  To escape characters in format

      strings, you can wrap the characters in square brackets: `{{ format

      lead.dob format="[It's] MMMM Do" }}` results in "It's October 12th".


      #### Date Format Tokens


      |                            | Token                      | Output     |

      | -------------------------- | -------------------------- | -----------|

      | Month                      | `M`                          | 1 2 ... 11
      12

      |                            | `Mo`                         | 1st 2nd ...
      11th 12th

      |                            | `MM`                         | 01 02 ... 11
      12

      |                            | `MMM`                        | Jan Feb ...
      Nov Dec

      |                            | `MMMM`                       | January
      February ... November December

      | Quarter                    | `Q`                          | 1 2 3 4

      |                            | `Qo`                         | 1st 2nd 3rd
      4th

      | Day of Month               | `D`                          | 1 2 ... 30
      31

      |                            | `Do`                         | 1st 2nd ...
      30th 31st

      |                            | `DD`                         | 01 02 ... 30
      31

      | Day of Year                | `DDD`                        | 1 2 ... 364
      365

      |                            | `DDDo`                       | 1st 2nd ...
      364th 365th

      |                            | `DDDD`                       | 001 002 ...
      364 365

      | Day of Week                | `d`                          | 0 1 ... 5 6

      |                            | `do`                         | 0th 1st ...
      5th 6th

      |                            | `dd`                         | Su Mo ... Fr
      Sa

      |                            | `ddd`                        | Sun Mon ...
      Fri Sat

      |                            | `dddd`                       | Sunday
      Monday ... Friday Saturday

      | Day of Week (Locale)       | `e`                          | 0 1 ... 5 6

      | Day of Week (ISO)          | `E`                          | 1 2 ... 6 7

      | Week of Year               | `w`                          | 1 2 ... 52
      53

      |                            | `wo`                         | 1st 2nd ...
      52nd 53rd

      |                            | `ww`                         | 01 02 ... 52
      53

      | Week of Year (ISO)         | `W`                          | 1 2 ... 52
      53

      |                            | `Wo`                         | 1st 2nd ...
      52nd 53rd

      |                            | `WW`                         | 01 02 ... 52
      53

      | Year                       | `YY`                         | 70 71 ... 29
      30

      |                            | `YYYY`                       | 1970 1971
      ... 2029 2030

      |                            | `Y`                         | 1970 1971 ...
      9999 +10000 +10001 Note: This complies with the ISO 8601 standard for
      dates past the year 9999

      | Week Year                  | `gg`                         | 70 71 ... 29
      30

      |                            | `gggg`                       | 1970 1971
      ... 2029 2030

      | Week Year (ISO)            | `GG`                         | 70 71 ... 29
      30

      |                            | `GGGG`                       | 1970 1971
      ... 2029 2030

      | AM/PM                      | `A`                          | AM PM

      |                            | `a`                          | am pm

      | Hour                       | `H`                          | 0 1 ... 22
      23

      |                            | `HH`                         | 00 01 ... 22
      23

      |                            | `h`                          | 1 2 ... 11
      12

      |                            | `hh`                         | 01 02 ... 11
      12

      |                            | `k`                          | 1 2 ... 23
      24

      |                            | `kk`                         | 01 02 ... 23
      24

      | Minute                     | `m`                          | 0 1 ... 58
      59

      |                            | `mm`                         | 00 01 ... 58
      59

      | Second                     | `s`                          | 0 1 ... 58
      59

      |                            | `ss`                         | 00 01 ... 58
      59

      | Fractional Second          | `S`                          | 0 1 ... 8 9

      |                            | `SS`                         | 00 01 ... 98
      99

      |                            | `SSS`                        | 000 001 ...
      998 999

      |                            | `SSSS ... SSSSSSSSS`         | 000[0..]
      001[0..] ... 998[0..] 999[0..]

      | Time Zone                  | `z` or `zz`                  | EST CST ...
      MST PST (requires use of the `timezone` option)

      |                            | `Z`                          | -07:00
      -06:00 ... +06:00 +07:00

      |                            | `ZZ`                         | -0700 -0600
      ... +0600 +0700

      | Unix Timestamp             | `X`                          | 1360013296

      | Unix Millisecond Timestamp | `x`                          |
      1360013296123


      #### Localized Date Formats


      Because preferred formatting differs based on locale, there are a few

      tokens that can be used to format a moment based on its locale.  There

      are upper and lower case variations on the same formats. The lowercase

      version is intended to be the shortened version of its uppercase

      counterpart.  To change the locale, use the `locale` options: `{{

      format date format="LLL" locale="fr" }}` results in "24 june 2015 17:24".


      |                                      | Format string | Output          |

      | ------------------------------------ | ------------- | ----------------|

      | Time                                 | `LT`            | 8:30 PM

      | Time with seconds                    | `LTS`           | 8:30:25 PM

      | Month numeral, day of month, year    | `L`             | 09/04/1986

      |                                      | `l`             | 9/4/1986

      | Month name, day of month, year       | `LL`            | September 4,
      1986

      |                                      | `ll`            | Sep 4, 1986

      | Month name, day of month, year, time | `LLL`           | September 4,
      1986 8:30 PM

      |                                      | `lll`           | Sep 4, 1986
      8:30 PM

      | Month name, day of month, day of week, year, time | `LLLL` | Thursday,
      September 4, 1986 8:30 PM

      |                                      | `llll`          | Thu, Sep 4,
      1986 8:30 PM


      ### Number Variable Formatting


      To format a number, use the `format` helper: `{{ format

      lead.mortgage.first_mortgage_balance format="$0,0.00"}}` results in

      '$45,302.00'. The `format` option is a string which defines the format

      of the number. See the table of examples below:


      | Number     | Format       | String      |

      | ---------- | ------------ | ----------- |

      | 10000      | `0,0.0000`   | 10,000.0000

      | 10000.23   | `0,0`        | 10,000

      | 10000.23   | `+0,0`       | +10,000

      | -10000     | `0,0.0`      | -10,000.0

      | 10000.1234 | `0.000`      | 10000.123

      | 100.1234   | `00000`      | 00100

      | 1000.1234  | `000000,0`   | 001,000

      | 10         | `000.00`     | 010.00

      | 10000.1234 | `0[.]00000`  | 10000.12340

      | -10000     | `(0,0.0000)` | (10,000.0000)

      | -0.23      | `.00`        | -.23

      | -0.23      | `(.00)`      | (.23)

      | 0.23       | `0.00000`    | 0.23000

      | 0.23       | `0.0[0000]`  | 0.23

      | 1230974    | `0.0a`       | 1.2m

      | 1460       | `0 a`        | 1 k

      | -104000    | `0a`         | -104k

      | 1          | `0o`         | 1st

      | 100        | `0o`         | 100th

      | 1000.234   | `$0,0.00`    | $1,000.23

      | 1000.2     | `0,0[.]00 $` | 1,000.20 $

      | 1001       | `$ 0,0[.]00` | $ 1,001

      | -1000.234  | `($0,0)`     | ($1,000)

      | -1000.234  | `$0.00`      | -$1000.23

      | 1230974    | `($ 0.00 a)` | $ 1.23 m


      Use the `locale` option to format the number to a particular locale:

      `{{ format lead.mortgage.first_mortgage_balance locale="fr"

      format="$0,0.00" }}` results in '€45 302.00'.


      ### Converting Number and Boolean to String


      Fields of type number and boolean can be converted to string using the
      `format` helper with the `dataType="String"` option:


      `{{format field_name dataType="String"}}`


      Example:

      - Input: `{{format lead.age dataType="String"}}` with `lead.age` = 30

      - Output: "30"


      ### Converting String to Number


      Fields whose final result from the `format` helper is a string can be
      converted to a number if the `dataType="Number"` option is passed and the
      value is a string representing a valid number.


      Examples:

      - Input: `{{format lead.postal_code dataType="Number"}}` with
      `lead.postal_code` = '78751'

      - Output: 78751 (as a number value)


      If the string does not represent a valid number, the result will not be
      converted:

      - Input: `{{format lead.postal_code dataType="Number"}}` with
      `lead.postal_code` = 'H3Z 2Y7'

      - Output: 'H3Z 2Y7'


      This can also be used with dates if the formatting returns only numbers,
      such as epoch time:

      - Input: `{{format lead.source_timestamp format="X" dataType="Number"}}`
      with `lead.source_timestamp` = '2015-06-24T17:24:49.060Z'

      - Output: 1435166689 (as a number value)



      ## Variable String Manipulation


      Strings can be manipulated with the following helpers:


      ### Lowercase


      Transforms the value to lowercase:


      `{{lowercase field_name}}`


      Example:

      - Input: `{{lowercase lead.first_name}}` with `lead.first_name` = "Mike"

      - Output: "mike"


      ### Uppercase


      Transforms the value to uppercase:


      `{{uppercase field_name}}`


      Example:

      - Input: `{{uppercase lead.first_name}}` with `lead.first_name` = "Mike"

      - Output: "MIKE"


      ### Substring


      Returns a substring from the start position to the end position, or from
      the start position to the end of the string if no end position is
      provided:


      `{{substring field_name start="3"}}`


      Example:

      - Input: `{{substring lead.first_name start="3"}}` with `lead.first_name`
      = "Michael"

      - Output: "chael"


      `{{substring field_name start="2" end="4"}}`


      Example:

      - Input: `{{substring lead.first_name start="2" end="4"}}` with
      `lead.first_name` = "Michael"

      - Output: "ich"


      ### Replace


      Replaces the pattern with the content of the replace option. The pattern
      can be a literal string or a regular expression using the `regexp()`
      option:


      `{{replace field_name pattern="Mi" replace="At"}}`


      Example:

      - Input: `{{replace lead.first_name pattern="Mi" replace="At"}}` with
      `lead.first_name` = "Michael"

      - Output: "Atchael"


      `{{replace field_name pattern="regexp(h.{3})" replace="ke"}}`


      Example:

      - Input: `{{replace lead.first_name pattern="regexp(h.{3})"
      replace="ke"}}` with `lead.first_name` = "Michael"

      - Output: "Micke"


      ### Extract


      Extracts all occurrences found with the regular expression in the pattern:


      `{{extract field_name
      pattern="\$begin:math:display$(.*?)\\$end:math:display$"}}`


      Example:

      - Input: `{{ extract foo pattern="(?<=#)(\\w+)(?=#)" }}` with
      `lead.first_name` = "#John#Doe#Smith"

      - Output: "John Doe"


      ## Variable Math


      To perform math operations, use the `math` helper: `{{ math "1 + 1" }}`

      results in `2`. Of course, variables can also be used: `{{ math "1 +

      lead.random_number" }}` might result in `32` depending on the value

      of `lead.random_number`. The math expression accepts a pretty basic

      grammar. Operators have the normal precedence:


      | Operator                 | Associativity | Description

      | ------------------------ | ------------- | ----------

      | (...)                    | None          | Grouping

      | f(), x.y                 | Left          | Function call, property
      access

      | !                        | Left          | Factorial

      | ^                        | Right         | Exponentiation

      | +, -, not, sqrt, etc.    | Right         | Unary prefix operators (see
      below for the full list)

      | \*, /, %                 | Left          | Multiplication, division,
      remainder

      | +, -, \|\|               | Left          | Addition, subtraction,
      concatenation

      | `==`, `!=`, `>=`, `<=`, `>`, `<`, `in` | Left          | Equals, not
      equals, etc. `in` means "is the left operand included in the right array
      operand?" (disabled by default)

      | and                      | Left          | Logical AND

      | or                       | Left          | Logical OR

      | x ? y : z                | Right         | Ternary conditional (if x
      then y else z)


      There are also several pre-defined functions:


      | Function      | Description |

      | ------------- | ----------- |

      | sin(x)        | Sine of x (x is in radians)

      | cos(x)        | Cosine of x (x is in radians)

      | tan(x)        | Tangent of x (x is… well, you know)

      | asin(x)       | Arc sine of x (in radians)

      | acos(x)       | Arc cosine of x (in radians)

      | atan(x)       | Arc tangent of x (in radians)

      | sqrt(x)       | Square root of x. Result is NaN (Not a Number) if x is
      negative.

      | log(x)        | Natural logarithm of x (not base-10). It’s log instead
      of ln because that’s what JavaScript calls it.

      | abs(x)        | Absolute value (magnitude) of x

      | ceil(x)       | Ceiling of x — the smallest integer that’s >= x.

      | floor(x)      | Floor of x — the largest integer that’s `<=` x

      | round(x)      | X, rounded to the nearest integer, using "grade-school
      rounding"

      | roundTo(x, n) | Rounds x to n places after the decimal point

      | exp(x)        | ex (exponential/antilogarithm function with base e)

      | random(n)     | Get a random number in the range [0, n). If n is zero,
      or not provided, it defaults to 1.

      | fac(n)        | n! (factorial of n: “n * (n-1) * (n-2) * … * 2 * 1″)

      | min(a,b,...)  | Get the smallest (“minimum”) number in the list

      | max(a,b,...)  | Get the largest (“maximum”) number in the list

      | pyt(a, b)     | Pythagorean function, i.e. the c in “c2 = a2 + b2“

      | pow(x, y)     | xy. This is exactly the same as “x^y”. It’s just
      provided since it’s in the Math object from JavaScript

      | atan2(y, x)   | arc tangent of x/y. i.e. the angle between (0, 0) and
      (x, y) in radians

      | if(c, a, b)   | Function form of c ? a : b


      #### Example


      To calculate the loan-to-value ratio, given a mortgage loan amount and

      the value of the home: `{{ math "(lead.mortgage.loan.amount /

      lead.mortgage.new_property_value) * 100" }}%`. Note that this example

      expresses the LTV as a percentage, first by calculating the percentage

      and then by appending the `%` character outside the variable

      placeholder. This could instead be handled using

      [formatting](#variable-math-formatting).


      ### Variable Math Formatting


      The `math` helper supports the same options as the `format` helper for

      numbers: `format` and `locale`. For example, to calculate the

      loan-to-value ratio and format it as a percentage: `{{ math

      "lead.mortgage.loan.amount / lead.mortgage.new_property_value"

      format="0.[00]%" }}`. This would return the LTV percentage with up to 2

      decimal points (i.e. 72.93%) as a string value.


      ## Variable Hashing


      LeadConduit supports a wide variety of hashing functions that can be

      applied to variables in a template. The helper name determines the

      hashing algorithm. For example, to use MD5 to hash the email address use:

      `{{ md5 lead.email }}`.  All the following hashing algorithms are

      supported:
        
      * md4

      * md5

      * ripemd

      * ripemd160

      * sha1

      * sha224

      * sha256

      * sha384

      * sha512

      * whirlpool
        
      Multiple values can be hashed together: `{{ md5 lead.email lead.phone_1
      }}`. This can be used to salt the hash 

      also: `{{ md5 lead.email "this is my salt" }}`. The `salt` option can also
      be used. This is the equivalent of the 

      last example: `{{ md5 lead.email salt="this is my salt" }}`.  


      Hashing supports multiple encodings using the `encoding` option:

      `{{ md5 lead.email encoding="base64" }}` results in something like

      "tkK0IXs0sejTvZFfxlxEUg==". The following encodings are supported:
        
      * `hex` (default)

      * `base64`

      * `latin1`
externalDocs:
  url: https://developers.activeprospect.com/
  description: ActiveProspect Developer Portal
paths:
  /feedback:
    get:
      tags:
        - Feedback
      summary: Submit feedback for a lead using query parameters
      description: >
        An alternative to `POST /feedback` for senders that cannot issue a
        request body. The

        feedback fields are read from the query string alongside `event_id`.
        Behavior is

        otherwise identical to the `POST` form, including the caveat that a
        `201` reports the

        submission was processed, not that it was accepted.
      operationId: submitFeedbackByQuery
      parameters:
        - in: query
          name: event_id
          required: true
          description: >-
            The ID of the delivery event the feedback refers to, shared with the
            recipient when the lead was delivered.
          schema:
            $ref: '#/components/schemas/ID'
        - in: query
          name: type
          required: true
          description: The kind of feedback being given.
          schema:
            type: string
            enum:
              - conversion
              - return
        - in: query
          name: reason
          required: false
          description: >
            The lifecycle stage the lead reached, or the reason it is being
            returned. Optional,

            but usually what the step's acceptance criteria are written against.
          schema:
            type: string
            example: Sale Closed
        - in: query
          name: occurred_at
          required: false
          description: >-
            When the status changed in your system, as an ISO 8601 timestamp.
            Defaults to the time LeadConduit received the feedback.
          schema:
            type: string
            example: '2026-05-30T08:00:00Z'
      responses:
        '201':
          description: >
            The feedback submission was processed. Check the `outcome` field to
            see whether it

            was accepted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackSubmissionResult'
            application/xml:
              schema:
                $ref: '#/components/schemas/FeedbackSubmissionResult'
            text/xml:
              schema:
                $ref: '#/components/schemas/FeedbackSubmissionResult'
        '400':
          description: >-
            The `type` query parameter is missing, or is not one of `conversion`
            and `return`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackSubmissionResult'
        '403':
          description: >-
            Feedback has not been configured on the flow step that delivered the
            lead.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Feedback not available in this flow
        '404':
          description: >-
            No event was found for the given `event_id`, or the event is more
            than 90 days old.
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                    example: Event not found
        '406':
          description: The `Accept` header asks for a format that cannot be produced.
          content:
            text/plain:
              schema:
                type: string
                example: >-
                  Not capable of generating content according to the Accept
                  header
        '409':
          description: Feedback is configured on the flow step but currently switched off.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FeedbackSubmissionResult'
        '422':
          description: >-
            The `event_id` is not a valid ID, or it refers to the wrong kind of
            event.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: A Lead was ID provided, but feedback requires an event ID
      security: []
components:
  schemas:
    ID:
      type: string
      description: 24 character alpha-numeric BSON identifier
      pattern: ^[0-9a-fA-F]{24}$
      readOnly: true
      example: 5fd4371e940df5a34a3888b2
    FeedbackSubmissionResult:
      description: >
        The result of a feedback submission. Returned as JSON by default, or as
        XML

        (wrapped in a `<result>` root element) when requested via the `Accept`
        header.
      type: object
      properties:
        outcome:
          description: >
            Whether the feedback was accepted. Check this field rather than the
            status

            code — a rejected submission is still reported with `201`.
          type: string
          enum:
            - success
            - failure
            - error
          x-enumDescriptions:
            success: The feedback was accepted and recorded
            failure: >-
              The feedback was rejected, either by the step's acceptance
              criteria or because the lead had already been returned
            error: An error occurred while processing the feedback
          example: success
        reason:
          description: >-
            The reason the feedback was not recorded, when the outcome is not
            success.
          type:
            - string
            - 'null'
        price:
          description: >
            The price paid to the recipient for the lead. Present only when the
            delivery

            step priced the lead.
          type: number
        lead:
          description: Identifying details of the lead the feedback applies to.
          type: object
          properties:
            id:
              $ref: '#/components/schemas/ID'
            first_name:
              type: string
              example: Joe
            last_name:
              type: string
              example: Blow
            email:
              type: string
              example: joeblow@gmail.com
            phone_1:
              type: string
              example: '5125551212'
  securitySchemes:
    APIKey:
      type: http
      scheme: basic
      description: >
        LeadConduit uses [HTTP Basic
        Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)

        with the username `API` and your API key as the password.


        For example: `API:1f1b96c9150d8050e858c043d543bb4eadae0e6f`'

````