# Leads Viewer API - LLM Reference # Base URL: https://leads.qubeseptic.com (or your deployment URL) # Version: 2025.12.30 ## Authentication All API endpoints require Bearer token authentication: ``` Authorization: Bearer lv_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` API keys are created at /api-keys page. Keys work for: - Lead Submission API (`/api/v1/submissions/*`) - Teams API (`/api/teams/*`) - for programmatic team management --- ## Lead Submission API ### POST /api/v1/submissions/single Submit a single property lead for processing. Request: ```json { "address": "123 Main St", "city": "Austin", "state": "TX", "zip_code": "78701", "county": "Travis", "first_name": "John", "last_name": "Doe", "primary_phone": "512-555-1234", "email_1": "john@example.com", "project_description": "New septic installation", "permit_status": "Pending", "property_type": "residential", "lot_size": "2.5 acres", "bedrooms": 4, "source": "permit_scrape", "related_url": "https://permits.traviscountytx.gov/permit/12345", "team_id": 1 } ``` Response (200): ```json { "id": 1, "status": "completed", "result_lead_id": 42, "result_action": "created", "validation_warnings": [] } ``` ### POST /api/v1/submissions/batch Submit multiple leads in one request. Request: ```json { "leads": [ { "address": "123 Main St", "city": "Austin", "state": "TX", "first_name": "John", "property_type": "residential", "lot_size": "1.5 acres", "bedrooms": 3, "source": "permit_scrape", "related_url": "https://permits.traviscountytx.gov/permit/12345" }, { "address": "456 Oak Ave", "city": "Dallas", "state": "TX", "first_name": "Jane", "property_type": "commercial", "lot_size": "5 acres", "source": "referral" } ], "process_immediately": true, "team_id": 1 } ``` Note: `team_id` at batch level applies to all leads. Per-lead `team_id` overrides batch level. Response (200): ```json { "batch_id": "batch_abc123", "total": 2, "succeeded": 2, "failed": 0, "duplicates": 0, "results": [ {"index": 0, "status": "success", "lead_id": 42, "action": "created"}, {"index": 1, "status": "success", "lead_id": 43, "action": "created"} ], "processing_time_ms": 234 } ``` ### GET /api/v1/submissions/status/{submission_id} Check processing status of a submission. --- ## Lead Submission Fields All fields are optional. The system processes whatever data is provided. ### Property Info - address: string - Street address - city: string - City name - state: string - State code (TX, CA, etc.) - zip_code: string - ZIP code - county: string - County name ### Owner Info - first_name: string - Owner first name - last_name: string - Owner last name ### Contact Info - primary_phone: string - Primary phone number - primary_phone_type: string - mobile, landline, voip - phone_2 through phone_5: string - Additional phones - email_1 through email_3: string - Email addresses ### Mailing Address (if different from property) - mail_address: string - mail_city: string - mail_state: string - mail_zip: string ### Permit/Project Info - project_number: string - Permit/project ID - permit_date: string - Date permit filed - permit_status: string - Pending, Approved, In-Review, etc. - parcel_number: string - Tax parcel ID - subdivision: string - Subdivision name - lot: string - Lot number - work_type: string - Type of work - project_description: string - Description of project - specific_use: string - Property use type ### Property Details - property_type: string - residential, commercial, agricultural, vacant_land - lot_size: string - Acreage or lot size (e.g., "2.5 acres", "10,000 sqft") - bedrooms: int - Number of bedrooms (helps determine septic system capacity) - related_url: string - URL to permit portal, property listing, CAD record ### Geolocation - latitude: float - Latitude coordinate (auto-geocoded if not provided) - longitude: float - Longitude coordinate (auto-geocoded if not provided) ### Source Tracking - source: string - Lead source/channel (permit_scrape, referral, website, cold_call) - source_identifier: string - Your external reference ID ### Team Assignment - team_id: int - Assign lead to a specific team (optional) --- ## Lead Source Business API For importing businesses that might buy leads (plumbers, HVAC, contractors). ### POST /api/v1/lead-sources/import Bulk import businesses. Request: ```json { "businesses": [ { "name": "ABC Plumbing", "phone": "512-555-1234", "website": "https://abc-plumbing.com", "address": "123 Commerce St", "city": "Austin", "state": "TX", "zip_code": "78701", "industry": "plumbing", "rating": 4.5, "review_count": 120 } ], "relationship_type": "prospect", "skip_duplicates": true, "auto_categorize": true } ``` Response (200): ```json { "imported_count": 1, "skipped_count": 0, "error_count": 0, "imported_ids": [1] } ``` ### GET /api/v1/lead-sources/businesses List imported businesses with filters. Query params: - relationship_type: lead_buyer, lead_source, marketing_partner - industry: plumbing, hvac, real_estate, etc. - status: prospect, active, inactive - state: TX, CA, etc. - limit: int (default 50, max 500) - offset: int ### GET /api/v1/lead-sources/statistics Get counts by relationship type, industry, state, status. --- ## Processing Pipeline Submitted leads go through: 1. **Validation** - Data quality checks, normalization 2. **Duplicate Detection** - Match by address 3. **Geocoding** - Convert address to lat/lng 4. **CAD Enrichment** - Add county appraisal data --- ## Status Values Submission status: - pending: Waiting to process - processing: Currently in pipeline - completed: Successfully imported - failed: Processing error - duplicate: Skipped as duplicate Result action: - created: New lead created - updated: Existing lead updated - skipped: Lead skipped --- ## Error Responses 401 Unauthorized: ```json {"detail": "Invalid or expired API key"} ``` 422 Validation Error: ```json {"detail": [{"loc": ["body", "leads", 0, "state"], "msg": "Invalid state code"}]} ``` 429 Rate Limited: ```json {"detail": "Rate limit exceeded"} ``` --- ## Rate Limits - Per API key: 1000 requests/hour - Per IP: 100 requests/minute - Max batch size: 1000 leads --- ## Quick Examples ### cURL - Submit Single Lead ```bash curl -X POST "https://leads.qubeseptic.com/api/v1/submissions/single" \ -H "Authorization: Bearer lv_sk_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "address": "123 Main St", "city": "Boise", "state": "ID", "first_name": "John", "property_type": "residential", "lot_size": "2 acres", "bedrooms": 3, "source": "permit_scrape", "related_url": "https://permits.adacounty.id.gov/12345", "team_id": 1 }' ``` ### Python - Batch Submit with Team ```python import httpx response = httpx.post( "https://leads.qubeseptic.com/api/v1/submissions/batch", headers={"Authorization": "Bearer lv_sk_YOUR_KEY"}, json={ "leads": [ { "address": "123 Main St", "city": "Boise", "state": "ID", "property_type": "residential", "lot_size": "1.5 acres", "bedrooms": 4, "source": "permit_scrape", "related_url": "https://permits.adacounty.id.gov/12345" }, { "address": "456 Oak Ave", "city": "Meridian", "state": "ID", "property_type": "residential", "lot_size": "0.5 acres", "source": "referral" } ], "team_id": 1 # Assign all leads to team } ) print(response.json()) ``` --- ## Teams API Team-based collaboration for leads. Users see leads from their teams + unassigned leads. ### GET /api/teams/ List teams user belongs to (admins see all). Response (200): ```json [ { "id": 1, "name": "Qube Septic - Treasure Valley", "description": "Idaho septic leads team", "member_count": 3, "lead_count": 42, "is_active": true } ] ``` ### POST /api/teams/ Create a new team. Creator becomes owner. Request: ```json { "name": "My Team", "description": "Optional description" } ``` ### GET /api/teams/{id} Get team details. ### PATCH /api/teams/{id} Update team (owner/admin only). Request: ```json { "name": "New Name", "description": "New description" } ``` ### DELETE /api/teams/{id} Deactivate team (owner only). Does not delete data. --- ## Team Members ### GET /api/teams/{id}/members List team members. Response (200): ```json [ { "id": 1, "user_id": "keycloak-sub-id", "user_email": "mat@example.com", "user_name": "Mat Smith", "role": "owner", "is_active": true } ] ``` ### DELETE /api/teams/{id}/members/{user_id} Remove member (owner/admin only). Cannot remove owners. --- ## Team Invites Magic link invites for new team members. ### POST /api/teams/{id}/invites Send invite email (owner/admin only). Request: ```json { "email": "newuser@example.com", "role": "member" } ``` Response (200): ```json { "id": 1, "email": "newuser@example.com", "role": "member", "expires_at": "2025-01-05T12:00:00Z", "is_used": false } ``` ### GET /api/teams/{id}/invites List pending invites (owner/admin only). --- ## Public Invite Endpoints (No Auth) ### GET /api/invites/{token} Get invite info for acceptance page. Response (200): ```json { "team_name": "Qube Septic - Treasure Valley", "team_description": "Idaho septic leads team", "email": "newuser@example.com", "role": "member", "invited_by_name": "Mat Smith", "expires_at": "2025-01-05T12:00:00Z" } ``` ### POST /api/invites/{token}/accept Accept invite. Creates Keycloak account if needed, returns auth tokens. Request: ```json { "first_name": "Mike", "last_name": "Jones" } ``` Response (200): ```json { "success": true, "team_id": 1, "team_name": "Qube Septic - Treasure Valley", "access_token": "eyJ...", "refresh_token": "eyJ...", "expires_in": 3600 } ``` --- ## Team Roles - **owner**: Full control, can delete team, cannot be removed - **admin**: Can manage members and invites - **member**: Can view and work team leads --- ## User's Teams ### GET /api/teams/me/memberships Get current user's team memberships with role info. --- ## Related Documentation - Interactive API docs: /docs (Swagger UI) - OpenAPI spec: /openapi.json - API Keys management: /api-keys - Teams management: /teams