RoofIQ API Documentation

Complete API integration guide with enhanced leads creation, automatic duplicate detection, Google Maps validation, and intelligent agent assignment.

5 Core APIs
15 Endpoints
99.9% Uptime
✨ Enhanced API Example
// Enhanced single-call lead creation
const roofiq = new RoofIQAPI(apiKey);

// Create lead with automatic contact & property creation
const result = await roofiq.leads.create({
  contact: {
    first_name: "John",
    last_name: "Doe", 
    email: "john@example.com",
    mobile_number: "+1234567890"
  },
  property: {
    address: "123 Main St, Springfield, IL",
    property_type: "Residential"
    // Google Maps auto-fills city, state, zip, coordinates
  },
  pipeline_id: "pipeline-uuid",
  pipeline_stage_id: "stage-uuid",
  source: "Voice360"
});

// Check what was created vs reused
console.log("Contact created:", result.creation_details.contact.created);
console.log("Property created:", result.creation_details.property.created);

// 4. Schedule appointment 
const appointment = await roofiq.appointments.create({
  lead_id: lead.id,
  title: "Estimate Consultation",
  preferred_start_time: "2024-01-15T10:00:00",
  timezone: "America/New_York"
  // Customer info automatically retrieved from lead contact
});

Authentication

All API requests require authentication using API keys. Your API key carries many privileges, so keep it secure!

πŸ”

API Key Format

All RoofIQ API keys begin with roofiq_ followed by a unique identifier.

Authentication Methods

Authorization Header (Recommended)

Authorization: Bearer roofiq_your_api_key_here

X-API-Key Header

X-API-Key: roofiq_your_api_key_here

API Key Generation

Admin users can generate API keys in RoofIQ:

  1. Go to Settings β†’ API Keys in your RoofIQ dashboard
  2. Click "Generate New API Key"
  3. Select the permissions you need (see below)
  4. Copy and securely store your API key

Required Permissions

Select the permissions you need based on which endpoints you'll use:

contacts:create Create new contacts
contacts:read Search and retrieve contacts
properties:create Create new properties
properties:read Search and retrieve properties
leads:create Create new leads
leads:read Search and retrieve leads
appointments:create Schedule appointments
appointments:read Check availability and retrieve appointments

Quick Start Guide

Get up and running with the RoofIQ API in minutes. Follow this step-by-step guide to create your first lead.

1

Generate Your API Key

Admin users can generate API keys directly in RoofIQ under Settings β†’ API Keys. Set the permissions you need for your integration.

View Permissions
2

Test Authentication

Verify your API key works by making a test request:

Test API Connection
curl -X GET https://api.roofiq.net/api/v1/health \
  -H "Authorization: Bearer roofiq_your_api_key_here"
3

Create Your First Lead

Follow the complete workflow to create a lead with automatic agent assignment:

Contact β†’ Property β†’ Lead β†’ Appointment

Complete Integration Workflow

This example shows the complete end-to-end workflow for creating leads and scheduling appointments:

πŸ‘€

Step 1: Create Contact

Required

Create or lookup customer contact information. If a duplicate email exists, you'll get a specific error and should use the existing contact instead.

POST /api/public/v1/contacts
{
  "first_name": "John",
  "last_name": "Doe",
  "email": "john.doe@email.com",
  "phone": "+1234567890",
  "company": "ABC Corp"
}
🏠

Step 2: Create Property

Required

Create property record. If a duplicate address exists, you'll get a specific error and should search for the existing property.

POST /api/public/v1/properties
{
  "address": "123 Main Street",
  "city": "Springfield",
  "state": "IL", 
  "zip_code": "62701",
  "property_type": "Residential",
  "square_footage": 2500,
  "year_built": 1995
}
🎯

Step 3: Create Lead

Auto-Assignment

Create lead and trigger automatic agent assignment based on territory and availability.

POST /api/public/v1/leads
{
  "contact_id": "550e8400-e29b-41d4-a716-446655440001",
  "property_id": "550e8400-e29b-41d4-a716-446655440002", 
  "pipeline_id": "550e8400-e29b-41d4-a716-446655440003",
  "pipeline_stage_id": "550e8400-e29b-41d4-a716-446655440004",
  "source": "External API Integration",
  "notes": "Customer interested in roof replacement"
}
πŸ“…

Step 4: Schedule Appointment

Optional

Schedule consultation with the automatically assigned agent.

POST /api/public/v1/appointments
{
  "lead_id": "550e8400-e29b-41d4-a716-446655440005",
  "title": "Roof Estimate Consultation",
  "description": "Initial consultation for roof replacement estimate",
  "preferred_start_time": "2024-01-15T10:00:00",
  "duration_minutes": 60,
  "timezone": "America/Chicago",
  "appointment_type": "estimate_consultation",
  "location": "123 Main Street, Springfield, IL 62701"
}

Contacts API

Manage customer contact information with built-in duplicate checking.

POST /api/public/v1/contacts Create a new contact

Request Body

{
  "first_name": "string",     // Required
  "last_name": "string",      // Required  
  "email": "string",          // Required
  "phone": "string",          // Required
  "company": "string"         // Optional
}

Response

{
  "results": {
    "contact": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "first_name": "John",
      "last_name": "Doe",
      "email": "john@example.com",
      "phone": "+1234567890",
      "company": "ABC Corp",
      "created_at": "2024-01-15T10:30:00Z",
      "updated_at": "2024-01-15T10:30:00Z"
    },
    "message": "Contact created successfully"
  },
  "status": 201
}
GET /api/public/v1/contacts Search contacts

Query Parameters

email string Search by email address
phone string Search by phone number
first_name string Search by first name
last_name string Search by last name
limit integer Max results (default: 10, max: 100)
offset integer Pagination offset (default: 0)

Properties API

Manage property information with automatic duplicate prevention.

POST /api/public/v1/properties Create a new property

Request Body

{
  "address": "string",           // Required
  "city": "string",              // Required
  "state": "string",             // Required
  "zip_code": "string",          // Required
  "country": "string",           // Optional (default: "USA")
  "property_type": "string",     // Required: "Residential", "Commercial", "Industrial", "Multi-Family"
  "square_footage": "integer",   // Optional
  "year_built": "integer",       // Optional
  "parcel_number": "string",     // Optional
  "zoning_type": "string",       // Optional
  "notes": "string"              // Optional
}

Leads API

Create leads with automatic agent assignment based on territory and availability rules.

⚑

Automatic Agent Assignment

When you create a lead, RoofIQ automatically assigns it to the best available agent based on:

  • Territory rules - Geographic proximity and coverage areas
  • Load balancing - Current workload distribution
  • Availability - Agent schedules and capacity
  • Skills matching - Required expertise and specializations
POST /api/public/v1/leads Create a new lead
✨

Enhanced API Features

This API now supports optional nested contact and property creation with automatic duplicate detection and Google Maps address validation!

πŸ“– View Complete Enhanced API Documentation

Traditional Request Body (Backward Compatible)

{
  "contact_id": "uuid",              // Required - From contacts API
  "property_id": "uuid",             // Optional - From properties API  
  "pipeline_id": "uuid",             // Required - Get from account settings
  "pipeline_stage_id": "uuid",       // Required - Get from pipeline stages
  "source": "string",                // Optional - Lead source identifier
  "notes": "string"                  // Optional - Additional notes
}

Enhanced Request Body (New)

{
  "contact": {                       // Create contact inline
    "first_name": "John",
    "last_name": "Doe",
    "email": "john@example.com",
    "mobile_number": "+1234567890"
  },
  "property": {                      // Create property inline (optional)
    "address": "123 Main St",        // Google Maps will validate & geocode
    "property_type": "Residential"
  },
  "pipeline_id": "uuid",             // Required
  "pipeline_stage_id": "uuid",       // Required
  "source": "Voice360",              // Optional
  "notes": "Customer called about roof repair"
}

Traditional Response

{
  "results": {
    "lead": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "contact_id": "550e8400-e29b-41d4-a716-446655440001",
      "property_id": "550e8400-e29b-41d4-a716-446655440002",
      "pipeline_id": "550e8400-e29b-41d4-a716-446655440003",
      "pipeline_stage_id": "550e8400-e29b-41d4-a716-446655440004",
      "assigned_user_id": "550e8400-e29b-41d4-a716-446655440005",
      "assigned_user_name": "Sarah Johnson",
      "assigned_user_email": "sarah@roofingco.com",
      "created_at": "2024-01-15T10:30:00Z"
    },
    "message": "Lead created successfully"
  }
}

Enhanced Response (with creation details)

{
  "results": {
    "lead": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "contact_id": "550e8400-e29b-41d4-a716-446655440001",
      "property_id": "550e8400-e29b-41d4-a716-446655440002",
      "pipeline_id": "550e8400-e29b-41d4-a716-446655440003",
      "pipeline_stage_id": "550e8400-e29b-41d4-a716-446655440004",
      "assigned_user_id": "550e8400-e29b-41d4-a716-446655440005",
      "assigned_user_name": "Sarah Johnson", 
      "assigned_user_email": "sarah@roofingco.com",
      "created_at": "2024-01-15T10:30:00Z"
    },
    "message": "Lead created successfully",
    "creation_details": {
      "contact": {
        "created": true,              // true = new contact created
        "data": {                     // contact data (if created)
          "id": "550e8400-e29b-41d4-a716-446655440001",
          "first_name": "John",
          "last_name": "Doe",
          "email": "john@example.com"
        }
      },
      "property": {
        "created": false,             // false = existing property used
        "data": null                  // null when existing property used
      }
    }
  }
}
πŸ”

Duplicate Detection

  • Contacts: Matched by email (exact) or phone number (fuzzy matching)
  • Properties: Matched by address + city + state + zip code combination
  • Behavior: Returns existing records when duplicates are found
  • Response: Check creation_details to see what was created vs reused
πŸ—ΊοΈ

Google Maps Address Validation

  • Automatic Validation: Addresses are validated using Google Places API
  • Component Extraction: City, state, zip code auto-filled from Google data
  • Geocoding: Latitude and longitude automatically populated
  • Fallback: Works gracefully without Google Maps API if not configured

Enhanced Leads Integration Guide

Comprehensive examples and best practices for using the enhanced public leads API with automatic contact and property creation.

⚑

Integration Scenarios

The enhanced leads API is perfect for:

  • Voice360 AI: Convert call completions to leads with customer and property data
  • CRM Systems: Import leads with full contact and property information
  • Web Forms: Direct form submissions without pre-processing
  • Bulk Imports: Mass data migration with duplicate protection
POST /api/public/v1/leads Voice360 Integration Example

Voice360 Call Completion β†’ Lead Creation

{
  "contact": {
    "first_name": "Sarah",
    "last_name": "Johnson", 
    "email": "sarah.johnson@example.com",
    "mobile_number": "+1-555-0123"
  },
  "property": {
    "address": "789 Pine Street, Miami, FL 33101",
    "property_type": "Residential"
  },
  "pipeline_id": "550e8400-e29b-41d4-a716-446655440002",
  "pipeline_stage_id": "550e8400-e29b-41d4-a716-446655440003",
  "source": "Voice360",
  "notes": "Customer interested in roof repair after storm damage. Appointment requested."
}
🎯

What Happens Automatically

  • πŸ” Duplicate Check: Searches for existing contact by email/phone
  • πŸ—ΊοΈ Address Validation: Google Maps validates and geocodes the address
  • 🏠 Property Matching: Checks for existing property at same address
  • πŸ‘€ Agent Assignment: Automatically assigns lead to best available agent
  • πŸ“Š Pipeline Placement: Places lead in specified pipeline stage
POST /api/public/v1/leads CRM System Integration Example

External CRM β†’ RoofIQ Lead Import

{
  "contact": {
    "first_name": "Michael",
    "last_name": "Chen",
    "email": "michael.chen@example.com",
    "mobile_number": "555-0199",
    "company": "Chen Construction"
  },
  "property": {
    "address": "456 Oak Avenue, Tampa, FL 33602",
    "property_type": "Commercial",
    "square_footage": 5000,
    "year_built": 2005,
    "notes": "Large commercial building, flat roof"
  },
  "pipeline_id": "550e8400-e29b-41d4-a716-446655440002",
  "pipeline_stage_id": "550e8400-e29b-41d4-a716-446655440003",
  "source": "External CRM",
  "notes": "Imported from Salesforce - high-value commercial lead"
}
POST /api/public/v1/leads Minimal Web Form Example

Simple Contact Form β†’ Lead

{
  "contact": {
    "first_name": "Emma",
    "last_name": "Wilson",
    "email": "emma.wilson@example.com"
  },
  "property": {
    "address": "321 Maple Drive, Orlando, FL 32801"
    // City, state, zip auto-filled by Google Maps
  },
  "pipeline_id": "550e8400-e29b-41d4-a716-446655440002",
  "pipeline_stage_id": "550e8400-e29b-41d4-a716-446655440003"
}

Result: Google Maps will automatically fill city="Orlando", state="FL", zip_code="32801", plus latitude/longitude coordinates.

POST /api/public/v1/leads Contact-Only Lead Example

Phone Inquiry β†’ Lead (No Property Info)

{
  "contact": {
    "first_name": "David",
    "last_name": "Rodriguez",
    "email": "david.rodriguez@example.com", 
    "mobile_number": "+1-555-0155"
  },
  // No property object - lead created without property
  "pipeline_id": "550e8400-e29b-41d4-a716-446655440002",
  "pipeline_stage_id": "550e8400-e29b-41d4-a716-446655440003",
  "source": "Phone Inquiry",
  "notes": "Customer called asking about services, needs property inspection"
}
πŸ”§

Configuration

Required Setup

  1. API Key: Obtain API key with leads:create permission
  2. Pipeline IDs: Get your account's pipeline and stage IDs
  3. Google Maps (Optional): Set GOOGLE_MAPS_API_KEY environment variable

Pipeline & Stage IDs

Use the Pipelines API to get your account's pipeline and stage IDs:

GET /api/public/v1/pipelines
Authorization: Bearer your-api-key
πŸ“š

Best Practices

  • Error Handling: Always check creation_details in responses
  • Duplicate Strategy: Use consistent email formats for reliable duplicate detection
  • Address Quality: Provide complete addresses for best Google Maps results
  • Monitoring: Log whether contacts/properties are created vs reused
  • Testing: Test with real addresses to verify Google Maps integration
⚠️

Error Handling

Common Error Responses

Validation Error
{
  "errors": {
    "error_code": "validation_error",
    "errors": {
      "contact": {
        "email": ["Not a valid email address"]
      },
      "property": {
        "address": ["Missing data for required field"]
      }
    }
  }
}
Duplicate Contact Error
{
  "errors": {
    "error_code": "integrity_error",
    "errors": {
      "email": {
        "message": "A contact with this email already exists in this account.",
        "code": "duplicate_entry"
      }
    }
  }
}

Note: The enhanced API prevents duplicates automatically. These errors only occur with invalid data or system issues.

Appointments API

Schedule appointments with automatically assigned agents and check availability.

POST /api/public/v1/appointments Schedule appointment with assigned agent

Request Body

{
  "lead_id": "uuid",                    // Required - Lead with assigned agent
  "title": "string",                    // Required - Appointment title
  "preferred_start_time": "datetime",   // Required - ISO 8601 format
  "duration_minutes": "integer",        // Optional - Default: 60
  "timezone": "string",                 // Optional - Default: "UTC"
  "appointment_type": "string",         // Optional - Default: "estimate_consultation"
  "location": "string",                 // Optional - Meeting location
  "description": "string"              // Optional - Additional details
}
πŸ‘€

Customer Information

Customer details (name, email, phone) are automatically retrieved from the lead's associated contact record. This eliminates redundant data entry and ensures consistency.

POST /api/public/v1/leads/{lead_id}/agent-availability Check assigned agent availability

Request Body

{
  "start_date": "2024-01-15",           // Required - YYYY-MM-DD
  "end_date": "2024-01-22",             // Required - YYYY-MM-DD
  "duration_minutes": 60,               // Optional - Default: 60
  "timezone": "America/New_York",       // Required
  "appointment_type": "estimate_consultation", // Optional
  "max_slots": 20                       // Optional - Default: 20
}

Response

{
  "results": {
    "lead_id": "550e8400-e29b-41d4-a716-446655440000",
    "assigned_agent": {
      "id": "550e8400-e29b-41d4-a716-446655440005",
      "name": "Sarah Johnson", 
      "email": "sarah@roofingco.com"
    },
    "availability": {
      "available_slots": [
        {
          "start_time": "2024-01-15T10:00:00",
          "end_time": "2024-01-15T11:00:00",
          "duration_minutes": 60,
          "timezone": "America/New_York",
          "confidence_score": 0.9
        }
      ]
    }
  },
  "status": 200
}
GET /api/public/v1/leads/{lead_id}/agent-calendars Get assigned agent calendar info

Returns calendar information for the assigned agent to help understand scheduling constraints.

Pipelines API

Discover available pipelines and stages for lead management. Essential for integration setup.

πŸ”§

Integration Setup

Use these endpoints to populate dropdown lists in your application instead of requiring users to copy/paste pipeline UUIDs.

GET /api/public/v1/pipelines Get all pipelines and stages

Response

{
  "results": {
    "pipelines": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Residential Leads",
        "description": "For house roofing jobs", 
        "is_default": true,
        "stages": [
          {
            "id": "550e8400-e29b-41d4-a716-446655440001",
            "name": "New Lead",
            "position": 1
          },
          {
            "id": "550e8400-e29b-41d4-a716-446655440002",
            "name": "Contacted",
            "position": 2
          },
          {
            "id": "550e8400-e29b-41d4-a716-446655440003", 
            "name": "Estimate Scheduled",
            "position": 3
          }
        ]
      },
      {
        "id": "550e8400-e29b-41d4-a716-446655440004",
        "name": "Commercial Leads",
        "description": "For commercial roofing projects",
        "is_default": false,
        "stages": [
          {
            "id": "550e8400-e29b-41d4-a716-446655440005",
            "name": "Initial Contact",
            "position": 1
          },
          {
            "id": "550e8400-e29b-41d4-a716-446655440006",
            "name": "Site Survey",
            "position": 2
          }
        ]
      }
    ],
    "total_count": 2,
    "account_id": "550e8400-e29b-41d4-a716-446655440999"
  },
  "status": 200
}
GET /api/public/v1/pipelines/{pipeline_id} Get specific pipeline details

Response

{
  "results": {
    "pipeline": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Residential Leads",
      "description": "For house roofing jobs",
      "is_default": true,
      "stages": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440001",
          "name": "New Lead",
          "position": 1,
          "description": "Freshly received leads"
        },
        {
          "id": "550e8400-e29b-41d4-a716-446655440002", 
          "name": "Contacted",
          "position": 2,
          "description": "Customer has been contacted"
        }
      ]
    },
    "account_id": "550e8400-e29b-41d4-a716-446655440999"
  },
  "status": 200
}
πŸ’‘

Integration Workflow

Step 1: User enters API key in your app
Step 2: Your app calls GET /public/v1/pipelines
Step 3: Show dropdown: "Residential Leads", "Commercial Leads"
Step 4: User selects pipeline, show stage dropdown
Step 5: Store the UUIDs for lead creation

Error Handling

The RoofIQ API uses conventional HTTP response codes and returns detailed error information in JSON format.

200 OK - Request succeeded
201 Created - Resource created successfully
400 Bad Request - Invalid request parameters
401 Unauthorized - Invalid or missing API key
403 Forbidden - API key lacks required permissions
404 Not Found - Resource not found
422 Unprocessable Entity - Validation errors
429 Too Many Requests - Rate limit exceeded
500 Internal Server Error - Server error

Error Response Format

{
  "errors": {
    "error": "Validation error",
    "message": "Required fields missing: first_name, email",
    "details": {
      "first_name": ["This field is required"],
      "email": ["This field is required"]
    }
  },
  "status": 400
}

Duplicate Record Errors

When attempting to create records that already exist, you'll receive specific duplicate error responses:

Duplicate Contact (Email)

{
  "errors": {
    "error_code": "integrity_error",
    "errors": {
      "email": {
        "message": "A contact with this email already exists in this account.",
        "code": "duplicate_entry"
      }
    }
  },
  "status": 400
}

Duplicate Property (Address)

{
  "errors": {
    "error_code": "integrity_error", 
    "errors": {
      "address": {
        "message": "A property with this address already exists.",
        "code": "duplicate_entry"
      }
    }
  },
  "status": 400
}
πŸ’‘

Handling Duplicates

When you receive a duplicate error:

  • For Contacts: Use the GET endpoint to search by email and retrieve the existing contact ID
  • For Properties: Use the GET endpoint to search by address and retrieve the existing property ID
  • Then proceed with lead creation using the existing contact/property IDs

Rate Limits

API requests are rate limited to ensure fair usage and system stability.

Standard Tier

1,000 requests/hour

Default rate limit for all API keys

Premium Tier

5,000 requests/hour

Available upon request for high-volume integrations

Rate Limit Headers

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200

Support & Resources

Need help with your integration? We're here to support you.

πŸ“§

Email Support

Get technical help from our API team

Contact API Team
πŸ”‘

API Key Help

Need help generating or configuring API keys?

Get Help
πŸ“‹

Integration Review

Schedule a call to review your integration

Schedule Review