# Lead Generation

Search for businesses via Google Maps (powered by Apify), match against existing CRM records, save search configurations, and import results as contacts, customers, and deals. Requires lead-gen:read, lead-gen:write, or lead-gen:delete scopes.

**Base URL:** `https://api.trustpager.com/functions/v1/api/v1`

## Endpoints

### POST /lead-gen/search

Start a new Google Maps business search via Apify. For max_results <= 100 the search runs synchronously and returns results immediately. For 101-500 results, the search runs asynchronously -- poll GET /lead-gen/searches/:id until status is "completed". When scrape_contacts=true, the search always runs asynchronously (website scraping exceeds sync timeout), website_filter is forced to "withWebsite", and results without a valid email are dropped before saving (email guarantee) -- result_count reflects only kept rows. Results are automatically deduplicated and matched against existing CRM contacts (by phone) and customers (by website domain). Credits are charged based on max_results tier.

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/post-lead-gen-search.md)

### GET /lead-gen/searches

List past lead generation searches with cursor-based pagination, most recent first. Filter by status or saved search.

**Scopes:** `lead-gen:read` — [full detail](./lead-gen/get-lead-gen-searches.md)

### GET /lead-gen/searches/:id

Get a single search by ID including all results. For async searches (max_results > 100) still in "running" status, this endpoint automatically polls Apify and processes results if the run has completed. Poll every 30-60 seconds until status is "completed" or "failed". Results are sorted by Google rating (highest first).

**Scopes:** `lead-gen:read` — [full detail](./lead-gen/get-lead-gen-searches-id.md)

### POST /lead-gen/import

Import selected search results into the CRM. Each result creates one contact AND one customer, linked together via crm_contact_customers. A note activity is automatically logged recording the Google Maps source. Optionally creates a deal placed into a specified pipeline stage via crm_deal_pipeline_placements. Only results with imported=false are imported -- already-imported results are filtered out in code (not via a PostgREST filter, to correctly handle null values). Each imported record returns contact_id, customer_id, and deal_id (null if no pipeline provided). Partial failures are non-fatal: a failed individual record returns an error field instead of IDs and is excluded from imported_count.

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/post-lead-gen-import.md)

### POST /lead-gen/saved-searches

Create a saved search configuration for recurring use. Saved searches store the query, location, and default import settings. Run a saved search by calling POST /lead-gen/search with saved_search_id -- the run_count and last_run_at fields update automatically.

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/post-lead-gen-saved-searches.md)

### GET /lead-gen/saved-searches

List all active (non-archived) saved search configurations. Returns the most recently updated first.

**Scopes:** `lead-gen:read` — [full detail](./lead-gen/get-lead-gen-saved-searches.md)

### GET /lead-gen/saved-searches/:id

Get a single saved search configuration by ID.

**Scopes:** `lead-gen:read` — [full detail](./lead-gen/get-lead-gen-saved-searches-id.md)

### PUT /lead-gen/saved-searches/:id

Partial update a saved search configuration. Only provided fields are updated.

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/put-lead-gen-saved-searches-id.md)

### DELETE /lead-gen/saved-searches/:id

Archive (soft-delete) a saved search. The saved search will no longer appear in list results. Past searches linked to it are preserved. Returns 204 No Content on success.

**Scopes:** `lead-gen:delete` — [full detail](./lead-gen/delete-lead-gen-saved-searches-id.md)

### POST /lead-gen/results

Record a manual result for a lead-gen search result (e.g. mark as contacted, won, not interested). Creates an activity log entry on the linked contact if one exists.

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/post-lead-gen-results.md)

### POST /lead-gen/initiatives

Create a new outreach initiative (multi-step automated sequence). An initiative defines the name, goal, and optional daily send cap. Add steps via POST /lead-gen/initiatives/:id/steps, then enrol leads via POST /lead-gen/initiatives/:id/enrol.

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/post-lead-gen-initiatives.md)

### GET /lead-gen/initiatives

List all outreach initiatives for your workspace. Returns initiatives ordered by creation date descending, with step and enrolment counts.

**Scopes:** `lead-gen:read` — [full detail](./lead-gen/get-lead-gen-initiatives.md)

### GET /lead-gen/initiatives/:id

Retrieve a single outreach initiative including its full step list and summary enrolment counts.

**Scopes:** `lead-gen:read` — [full detail](./lead-gen/get-lead-gen-initiatives-id.md)

### PATCH /lead-gen/initiatives/:id

Update an outreach initiative's name, description, status, or daily send cap. Set status to "active" to begin processing enrolments, "paused" to temporarily halt sends.

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/patch-lead-gen-initiatives-id.md)

### DELETE /lead-gen/initiatives/:id

Delete an outreach initiative. All associated steps and enrolments are also removed. Active enrolments in progress will be cancelled. Returns 204 No Content on success.

**Scopes:** `lead-gen:delete` — [full detail](./lead-gen/delete-lead-gen-initiatives-id.md)

### POST /lead-gen/initiatives/:id/steps

Add a new step to an outreach initiative. Steps are executed in step_number order. Supported action types: send_gmail_email (requires gmail_connection_id and subject/body templates with {{lead.*}} tokens), send_sms (requires sms body with {{lead.*}} tokens), notify_assigned_staff (sends an internal alert email to the enrolling user).

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/post-lead-gen-initiatives-id-steps.md)

### PATCH /lead-gen/initiatives/:id/steps/:stepId

Update a step's delay, config (subject/body templates), or reorder its step_number. Updating step_number re-sequences other steps automatically.

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/patch-lead-gen-initiatives-id-steps-stepId.md)

### DELETE /lead-gen/initiatives/:id/steps/:stepId

Remove a step from an initiative. Subsequent steps are re-numbered to fill the gap. Returns 204 No Content on success.

**Scopes:** `lead-gen:delete` — [full detail](./lead-gen/delete-lead-gen-initiatives-id-steps-stepId.md)

### POST /lead-gen/initiatives/:id/enrol

Enrol one or more search results into an outreach initiative. Each enrolment tracks progress through the initiative's steps. Leads already enrolled in this initiative are skipped (idempotent). Unsubscribed leads are silently excluded.

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/post-lead-gen-initiatives-id-enrol.md)

### GET /lead-gen/initiatives/:id/enrolments

List enrolments for an initiative. Returns each enrolment with the lead's details, current step number, status, and next scheduled action date.

**Scopes:** `lead-gen:read` — [full detail](./lead-gen/get-lead-gen-initiatives-id-enrolments.md)

### POST /lead-gen/initiatives/dispatcher/run

Manually trigger the initiative dispatcher for your workspace. The dispatcher processes all due enrolment tasks: sends emails, SMS messages, and staff notifications for active initiatives. Under normal operation this runs automatically via cron. Use this endpoint to trigger an immediate processing cycle, for testing, or to catch up after a pause. Returns a summary of tasks processed.

**Scopes:** `lead-gen:write` — [full detail](./lead-gen/post-lead-gen-initiatives-dispatcher-run.md)
