RichLeadDocumentation

Developer reference / v1

API and webhooks

Use your CRM's HTTP actions to manage RichLead leads, and subscribe to RichLead events to send changes back to your CRM.

Authentication and access

The main workspace owner creates API keys in Settings → API & webhooks. Copy the token when it is created; it cannot be retrieved later. Use a separate key for each CRM connection. Revocation takes effect on the next API request.

Base URL: https://richlead.site/backend/api/v1
Authorization: Bearer YOUR_WORKSPACE_API_KEY
Content-Type: application/json

Use production URLs without a trailing slash to avoid frontend URL normalization redirects. If you call Django directly during local development, use http://localhost:8000/api/v1/ with trailing slashes on endpoints.

Permissions: leads:read, leads:write, leads:delete, suppression:read, suppression:write, suppression:delete. Keys can only access their own workspace. Normal login tokens do not authenticate this API. Workspace keys do not authenticate account settings, sender mailboxes, or provider credentials.

API limit: 120 requests per minute across all keys in a workspace. A 429 response includes Retry-After. List results use {count,next,previous,results}, 50 records per page. Follow the next link or increment ?page=2 on your configured public base URL. Proxy requests retain the public /backend prefix.

Code examples

Choose an operation and language to see a complete request. Each example reads your workspace key from RICHLEAD_API_KEY, sets authentication, limits request time and checks the HTTP response. These are HTTP client examples, not a separate RichLead SDK.

1. Set your API key

Run examples on your server or development machine. Keep keys out of browser code, public repositories and screenshots. Use a key with the permission shown for the selected operation. Replace YOUR_WORKSPACE_API_KEY with the token you copied from Settings. Set the variable in the same terminal where you run the example.

macOS, Linux or Git Bash

export RICHLEAD_API_KEY='YOUR_WORKSPACE_API_KEY'

Windows PowerShell

$env:RICHLEAD_API_KEY = 'YOUR_WORKSPACE_API_KEY'
HTTP errors and safe retries
  • 400: Check the returned field validation errors.
  • 401: Check that the workspace key is valid, active and not expired.
  • 403: Check the key's permission for this operation.
  • 404: Check the record ID and workspace.
  • 409: Resolve the record conflict. An upsert mapping for a deleted lead needs a new external ID to recreate it.
  • 429: Wait for the duration in Retry-After before another request.
  • 5xx or network timeout: Retry cautiously with backoff. A timed-out write may already have succeeded. Use stable upsert IDs for repeatable CRM syncs.

Examples do not retry automatically. cURL prints response headers and the body; other clients report unsuccessful status codes. Successful deletes are handled without attempting to parse an empty JSON response.

Lead operations

MethodPathPermission
GET/leads or /leads/IDleads:read
POST/leadsleads:write
POST/leads/upsertleads:write
PATCH / PUT/leads/IDleads:write
DELETE/leads/IDleads:delete

Language examples: Search · Create · Update · Delete · CRM upsert.

Create requires name and email. Supported contact fields include company, title, niche, phone, website, linkedin_url, industry, location, employee_count, funding_amount, funding_round, funding_data, source, source_id, campaign, and status. Use PATCH for partial updates. Funding data must be a JSON object up to 8 KB. Email addresses are normalized and unique within a workspace.

Read-only fields include email verification status, ICP score, sender identity, creation time, and actual sent/reply timestamps. Changing a contact's email resets provider verification to unknown. CRM status updates cannot undo a blacklisted lead. Global suppression continues to block automatic outreach. These endpoints do not send outreach emails or spend lead-provider credits.

curl -X POST 'https://richlead.site/backend/api/v1/leads' \
  -H 'Authorization: Bearer YOUR_WORKSPACE_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Taylor Smith","email":"taylor@example.com","company":"Example","phone":"+15555550123"}'

curl -X PATCH 'https://richlead.site/backend/api/v1/leads/123' \
  -H 'Authorization: Bearer YOUR_WORKSPACE_API_KEY' \
  -H 'Content-Type: application/json' -d '{"title":"CEO","campaign":"GHL campaign"}'

curl -X DELETE 'https://richlead.site/backend/api/v1/leads/123' \
  -H 'Authorization: Bearer YOUR_WORKSPACE_API_KEY'

Custom contact fields

Send extra data in custom_fields, a JSON object with lowercase keys such as annual_revenue or crm_stage. Unknown custom keys are accepted without a definition. Defined workspace fields enforce their configured type. Up to 80 fields and 32 KB per lead are supported; nested JSON is limited to four levels. Custom data is returned by the API and included in lead webhooks. Updating custom_fields replaces the object, so merge the previous values first when needed.

{"custom_fields":{"annual_revenue":1000000,"crm_stage":"Qualified","tags":["partner"]}}

Stable external IDs and retries

Use upsert when syncing CRM records. Supply a stable source name and the CRM's record ID. Repeating the same request updates the existing mapping instead of creating another contact. A matching workspace email can be linked to an existing lead. Save the returned lead.id in your CRM for later updates/deletes.

POST /leads/upsert
{
  "source_system": "ghl",
  "external_id": "CRM_CONTACT_ID",
  "lead": {"name":"Taylor Smith","email":"taylor@example.com","company":"Example"}
}

Response: {"created":true,"source_system":"ghl","external_id":"CRM_CONTACT_ID","lead":{"id":123,...}}

GET /leads?source_system=ghl&external_id=CRM_CONTACT_ID
GET /leads?email=taylor%40example.com
GET /leads?search=Taylor&status=pending
GET /leads?interest=interested
GET /leads?interest=hot

Interest filters: interested (includes hot), hot, warm, low_interest, not_interested, unknown. Responses include read-only interest, interest_score and reply_intent. Current AI assessments are used when fresh; otherwise the latest received reply is used. A recent opt-out or negative reply overrides older positive evidence. These are interest estimates, not sales outcomes or permission to bypass suppression.

Upsert returns 201 for a new lead and 200 for an existing lead. A deleted mapped lead leaves a tombstone: older upsert retries return 409 rather than silently recreating it. To intentionally recreate it, use a new external ID. Plain POST duplicate emails return a validation error; use upsert for repeatable synchronization. Updates follow last-write-wins semantics, so choose one source of truth for each mapped field.

Suppression

Use GET/POST on /suppression, and GET/PATCH/PUT/DELETE on /suppression/ID with the corresponding suppression permission. Supply either an email or a domain, not both.

{"email":"unsubscribed@example.com","reason":"unsubscribed","note":"Opted out in CRM"}
or
{"domain":"example.com","reason":"manual"}

Reasons: manual, unsubscribed, bounced, complained. Suppression is the real workspace suppression list, not a separate CRM flag. Removing an entry requires its own delete permission and does not automatically unblacklist a lead.

Language examples: List entries · Suppress email · Suppress domain · Remove entry.

Outgoing webhooks

Add a public HTTPS receiver on port 443, choose events, then Send test. Tests contain dummy data. Delivery happens in the background, normally within one minute. Private/local networks, credentials in the URL authority, fragments, and HTTP redirects are rejected. Configuration edits or secret rotation cancel older queued deliveries; you may explicitly replay a cancelled delivery to the current destination.

Events: lead.created, lead.updated, lead.deleted, lead.replied, suppression.created, suppression.updated, suppression.deleted. A selected event only includes records from that endpoint's workspace. Lead payloads contain contact fields, not generated email bodies or provider credentials.

{
  "id":"EVENT_UUID", "type":"lead.updated", "api_version":"v1",
  "occurred_at":"2026-10-15T12:00:00+00:00", "workspace_id":"123",
  "origin":{"api_key_id":"KEY_UUID","name":"GHL"},
  "data":{"id":123,"name":"Taylor Smith","email":"taylor@example.com",...}
}

Internal changes have origin:null. In Prevent sync loops, select the API key for the receiving CRM. RichLead will not send changes made by that key back to that endpoint. Unchanged lead saves do not generate update events. Keep stable IDs in your workflow and avoid bidirectional rules that repeatedly overwrite each other.

Verify delivery signatures

Headers: X-RichLead-Event, X-RichLead-Delivery, X-RichLead-Timestamp and X-RichLead-Signature. The signature is v1=HEX_HMAC_SHA256 using the endpoint signing secret over the timestamp, a dot, and the exact raw request bytes.

import hashlib, hmac, time

timestamp = request.headers['X-RichLead-Timestamp']
if abs(time.time() - int(timestamp)) > 300:
    reject_request()
expected = 'v1=' + hmac.new(
    signing_secret.encode(), timestamp.encode() + b'.' + request.body,
    hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers['X-RichLead-Signature']):
    reject_request()
# Persist the event ID uniquely before processing; duplicates return 2xx.

Delivery is at least once, not exactly once. A receiver may process a request whose response is lost, so deduplicate the event ID. Retries keep event/delivery IDs stable and use a fresh timestamp/signature. Return 2xx after durably accepting an event. Timeouts, network failures, 408, 429 and 5xx are retried with increasing delays, up to eight attempts. Other statuses, including redirects, fail immediately. Failed deliveries can be retried from Activity. Parallel deliveries may arrive out of order; reconcile using the current lead API record when needed.

CRM setup examples

GoHighLevel

Use a Custom Webhook action to POST the upsert JSON to RichLead with the Authorization header. Map the contact ID to external_id and contact fields into lead. Store RichLead's lead.id in a custom field. Choose CUSTOM mode for JSON and use PUT or DELETE with that ID for subsequent changes. PUT requires the name and email fields as well as the fields being changed. For the reverse direction, generate an Inbound Webhook workflow URL in GHL, add it as a RichLead endpoint, and map data fields in the workflow.

GHL Custom Webhook documentation

Zapier

Use Webhooks/API by Zapier to call the RichLead API with a Bearer header and JSON data. For RichLead events, create a Catch Hook trigger and paste its URL into RichLead. Add CRM actions after the trigger. Some webhook features may require a paid Zapier plan.

Zapier API request documentation

Zoho

Call the RichLead API from a Zoho function using invokeurl. Set the Authorization and Content-Type headers, method POST, and the upsert body. For RichLead events, use a Zoho Flow webhook trigger or an authenticated relay that verifies the signature and then calls Zoho's API. A native Zoho CRM API URL generally requires Zoho authentication; it is not a generic webhook receiver.

Zoho function documentation

Operations and privacy

Keys and webhooks are managed by the main customer owner. Team accounts cannot issue credentials or change destinations. Receivers and keys stay isolated per workspace. Disabled owners cannot use the API or deliver queued events. API logs contain key name, method, path, response status and time; authorization tokens and request/response bodies are not logged. Webhook payloads and delivery logs have a 30-day cleanup target. Pending deliveries are retained until resolved. Secrets are stored hashed (API keys) or encrypted (webhook signing secrets).

These are generic HTTP integrations, not pre-installed marketplace apps or automatic OAuth connections. Configure each provider's workflow and field mapping separately. Historical leads are available through the paginated API; webhooks cover future model changes after enabling an endpoint.