Data Model

Reference for every custom post type, post meta key, plugin option, and transient ApproxIt stores.

Custom Post Types

ax_estimator

The estimator builder CPT. Public-visible in the admin (shown as the “Estimators” menu) but not directly queryable from the frontend — embedding happens via the block or shortcode, which fetches by post ID.

PropertyValue
publictrue
show_uitrue
supportstitle
capability_typepost

ax_lead

Lead submissions. Hidden from public queries and the admin UI — surfaced only through the custom Leads dashboard.

PropertyValue
publicfalse
show_uifalse
supportstitle
capability_typepost

Post Meta — ax_estimator

Stored on the estimator post.

KeyTypeDescription
_ax_estimator_servicesarrayList of service rows. Each: { name, description, pricing_type, price, billing_period, unit_label, duration, addons[] }.
_ax_estimator_packagesarrayBundled packages. Each: { name, tagline, badge, price, billing_period, featured, items } where items is newline-separated.
_ax_estimator_questionsarrayProject questions (Pro feature, but persisted in Lite if loaded via template). Each row’s type is select, flat_fee_per_unit, or flat_fee_toggle.
_ax_estimator_stylearrayStyling. Keys: accent_color, step_title, step_subtitle.

Post Meta — ax_lead

Stored on the lead post when a customer submits.

Core (always set)

KeyTypeDescription
_ax_lead_emailstringCustomer email (sanitized via sanitize_email).
_ax_lead_phonestringCustomer phone (sanitized text). May be empty.
_ax_lead_addressstringCustomer address (sanitized textarea). May be empty.
_ax_lead_scorestringhot, warm, or cold.
_ax_lead_score_overridestring1 if admin manually overrode the auto-calculated score.
_ax_lead_statusstringnew, contacted, quoted, won, or lost.
_ax_lead_dataarrayFull estimate breakdown — see structure below.
_ax_lead_totalfloatHighest single-period total (used for sorting / display).
_ax_lead_periodstringBilling period of the highest total (e.g. weekly, one_time).
_ax_lead_estimator_idintSource estimator post ID.
_ax_lead_notesstringAdmin notes (sanitized textarea). Only set if notes were entered.

_ax_lead_data Shape

PHP

array(
    'estimator_id' => 42,
    'mode'         => 'service' | 'package',
    'services'     => array(
        array(
            'name'           => 'Lawn Mowing',
            'price'          => 45.0,
            'billing_period' => 'weekly',
            'pricing_type'   => 'fixed' | 'per_unit',
            'quantity'       => 1,
            'addons'         => array(
                array( 'label' => 'Edge Trimming', 'price' => 12.0 ),
            ),
        ),
    ),
    'package'      => null | array( 'name', 'price', 'billing_period' ),
    'totals'       => array(
        'weekly'   => 65.0,
        'one_time' => 0.0,
    ),
    'phone'        => '555-1234',
    'address'      => '123 Main St',
    'map_sqft'     => 0,       // Pro-only when map drawing used
    'map_path'     => '',      // Pro-only encoded polygon
)

Pro-Set Meta (only present in Pro builds with active license)

KeyTypeDescription
_ax_lead_photosarrayAttachment IDs for customer-uploaded photos.
_ax_lead_vision_resultsarrayAI photo analysis. Shape: { results: { att_id: { note, red_flags[], confidence, thumb } }, analyzed_at }.
_ax_lead_property_analysisarrayGemini property profile. Shape: { content, analyzed_at }.
_ax_lead_map_auditarrayMap area verification. Shape: { result: { deductions[], adjusted_sqft, confidence, summary }, verified_at }.
_ax_lead_semanticarraySemantic priority classification. Shape: { priority, points, reason, analyzed_at }.
_ax_lead_quote_sentstringISO datetime stamp when Pro’s “Send Quote” action ran.

Options (wp_options)

All option keys are prefixed ax_. Stored in the standard WP options table.

Lite Options

OptionTypeDefaultDescription
ax_from_namestringsite titleSender name in outgoing emails.
ax_from_emailstringadmin emailFrom address in outgoing emails.
ax_owner_emailstringadmin emailRecipient of owner alert emails.
ax_business_phonestringemptyBusiness phone shown in customer receipt.
ax_notify_customer_receiptstring'1'Whether customer receipts are sent ('1' or '').
ax_notify_owner_email_alertstring'1'Whether owner alerts are sent.
ax_rewrite_flushedstringemptyInternal: one-time-flush marker for rewrite rules.

Pro Options

OptionTypeDescription
ax_google_maps_keystringGoogle Maps API key.
ax_google_gemini_keystringGoogle Gemini API key.
ax_business_descriptionstringBusiness context for AI prompts.
ax_business_industrystringSelected industry slug from the dropdown.
ax_owner_phonestringOwner phone for SMS alerts (distinct from ax_business_phone).
ax_sendgrid_keystringSendGrid API key.
ax_twilio_sidstringTwilio account SID.
ax_twilio_tokenstringTwilio auth token.
ax_twilio_fromstringTwilio “From” phone number.
ax_notify_owner_smsstringToggle: SMS owner alerts on.
ax_nudge_enabledstringToggle: 30-minute follow-up SMS on.
ax_notify_abandonedstringToggle: abandoned-estimator alerts on.
ax_onboarding_dismissedstring'1' if user dismissed the Pro onboarding banner.

Transients (wp_options with _transient_ prefix)

TransientTTLPurpose
ax_lite_welcome24 hoursSet on activation; triggers the welcome admin notice once, then self-clears.
ax_rl_<md5(IP)>1 hourIP rate-limit counter — 3 submissions per hour from same IP.
ax_dup_<md5(email)>5 minutesDuplicate-submission throttle — same email once per 5 minutes.
ax_first_run_redirect1 minutePro post-activation welcome redirect flag.

Settings Group

All registered settings live in a single group: ax_settings_group. To register your own field that submits with ApproxIt’s settings form:

PHP

add_action( 'ax_register_settings', function () {
    register_setting( 'ax_settings_group', 'my_custom_option', array(
        'sanitize_callback' => 'sanitize_text_field',
    ) );
} );

add_action( 'ax_settings_tab_general', function () {
    // Render the input with name="my_custom_option" — it will save automatically.
} );

Querying Leads

Standard WordPress queries work for ax_lead. A typical “fetch this week’s hot leads”:

PHP

$leads = get_posts( array(
    'post_type'      => 'ax_lead',
    'post_status'    => 'publish',
    'posts_per_page' => -1,
    'date_query'     => array( array( 'after' => '1 week ago' ) ),
    'meta_query'     => array(
        array(
            'key'   => '_ax_lead_score',
            'value' => 'hot',
        ),
    ),
) );

foreach ( $leads as $lead ) {
    $email = get_post_meta( $lead->ID, '_ax_lead_email', true );
    $total = (float) get_post_meta( $lead->ID, '_ax_lead_total', true );
    // ...
}

For sorting by total value:

PHP

$leads = get_posts( array(
    'post_type'      => 'ax_lead',
    'meta_key'       => '_ax_lead_total',
    'orderby'        => 'meta_value_num',
    'order'          => 'DESC',
    'posts_per_page' => 20,
) );

Cleanup on Uninstall

uninstall.php runs only when the plugin is deleted (not deactivated). It:

  1. Deletes all posts of types ax_estimator and ax_lead, including their meta.
  2. Deletes named options (ax_owner_email, ax_from_name, ax_from_email, etc.).
  3. Sweeps any remaining ax_* options via wildcard SQL (catches Pro options and anything custom added via ax_register_settings).
  4. Sweeps all _transient_ax_* and _transient_timeout_ax_* entries.

If your integration adds options outside the ax_ namespace, you are responsible for cleaning them up in your own uninstall logic.