API Documentation

Comprehensive guides and references for seamless integration
Endpoint

POST https://localedgemarketingco.com/api?action=import_lead

Authentication

All API requests require authentication using a Bearer token in the Authorization header.

  • Header: Authorization: Bearer YOUR_API_KEY_HERE
  • Header: Content-Type: application/json

Getting Your API Key:

  1. Log into your CRM account
  2. Navigate to Settings, then Integrations, then API Key
  3. Generate a new API key or copy an existing one
  4. Keep your API key secure and never share it publicly

Rate Limits: API requests are limited to 100 requests per minute per API key.

Response Format & Versioning

Every response is JSON and follows one consistent shape. The success boolean tells you which envelope you received, and every response carries an api_version.

Success envelope:

{
	"success": true,
	"api_version": "1.0",
	... endpoint-specific fields
}
			

Error envelope: every failure includes a stable, machine-readable code you can branch on in your integration. The human-readable error text may change over time; the code will not.

{
	"success": false,
	"api_version": "1.0",
	"error": "Human-readable message",
	"code": "MACHINE_CODE"
}
			

Error codes:

  • MISSING_REQUIRED_FIELD (400): a required field is absent or malformed (missing name, no identifier, or an invalid lead_id).
  • UNAUTHORIZED (401): missing or invalid API key.
  • API_KEY_DISABLED (403): the account behind this key is deactivated.
  • LEAD_LIMIT_REACHED (403): your plan's lead limit is reached (import only).
  • NOT_FOUND (404): no lead on your account matches the identifier.
  • RATE_LIMIT_EXCEEDED (429): over 100 requests per minute; a retry_after value (in seconds) is included.
  • INTERNAL_ERROR (500): unexpected server error. Safe to retry with backoff.

Versioning: the current version is 1.0. Additive changes (new optional fields or new endpoints) keep the same major version and will not break existing integrations. Any breaking change to the wire contract is released under a new major version, so your integration keeps working unchanged. Read api_version if you need to detect which contract you received.

Sample Request Body
{
	"name": "Jane Smith",
	"email": "jane.smith@example.com",
	"phone": "5551234567",
	"source": "Zillow",
	"tags": ["zillow", "buyer", "new"],
	"addresses": [
		{
			"type": "home",
			"street": "123 Main St",
			"city": "Los Angeles",
			"state": "CA",
			"zip": "90001"
		},
		{
			"street": "456 Oak Ave",
			"city": "San Diego",
			"state": "CA",
			"zip": "92101"
		}
	],
	"stage": "new",
	"type": "buyer"
}
			
  • Required: name and at least one of email or phone
  • Optional: source, tags, addresses (array, each with type (optional, defaults to OTHER), street, city, state, zip), stage, type, etc.
Sample Success Response
{
	"success": true,
	"api_version": "1.0",
	"lead_id": "uuid-or-id-here",
	"action": "created" // or "updated"
}
			
Error Responses

All errors follow the shared error envelope and the codes listed in Response Format & Versioning above. The two cases specific to importing:

400 Bad Request - Missing Required Fields:

{
	"success": false,
	"api_version": "1.0",
	"error": "Missing required field: name",
	"code": "MISSING_REQUIRED_FIELD"
}
				

403 Forbidden - Lead Limit Reached:

{
	"success": false,
	"api_version": "1.0",
	"error": "Lead limit reached (500 of 500). Upgrade your plan to import more leads.",
	"code": "LEAD_LIMIT_REACHED"
}
				

429 Too Many Requests - Rate Limit Exceeded:

{
	"success": false,
	"api_version": "1.0",
	"error": "Rate limit exceeded. Maximum 100 requests per minute.",
	"code": "RATE_LIMIT_EXCEEDED",
	"retry_after": 60
}
				
Integration Instructions
  1. Obtain your API key from your CRM admin or settings page.
  2. Send a POST request to the endpoint above with the required headers and JSON body.
  3. On success, you will receive a JSON response with the lead ID and action taken.

You can use tools like Postman, cURL, Zapier, Make, or any programming language that supports HTTP requests.

Sample cURL Command
curl -X POST "https://localedgemarketingco.com/api?action=import_lead" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-d '{
"name": "Jane Smith",
"email": "jane.smith@example.com",
"phone": "5551234567",
"source": "Zillow",
"tags": ["zillow", "buyer", "new"],
"addresses": [
  {
	"type": "home",
	"street": "123 Main St",
	"city": "Los Angeles",
	"state": "CA",
	"zip": "90001"
  },
  {
	"street": "456 Oak Ave",
	"city": "San Diego",
	"state": "CA",
	"zip": "92101"
  }
],
"stage": "new",
"type": "buyer"
}'
			
Lead Status Endpoint

POST https://localedgemarketingco.com/api?action=get_lead_status

Check what has happened to a lead you previously sent. Uses the same Bearer API key as the import endpoint.

Request Body: provide any one of these identifiers.

  • lead_id (the id returned by import_lead), or
  • email, or
  • phone
{
	"lead_id": "uuid-of-the-lead"
}
			

Sample Success Response:

{
	"success": true,
	"api_version": "1.0",
	"lead": {
		"lead_id": "uuid-or-id-here",
		"name": "Jane Smith",
		"stage": "Contacted",
		"type": "Buyer",
		"source": "Zillow",
		"tags": ["zillow", "buyer"],
		"created_at": "2026-07-23 14:07:57+00",
		"updated_at": "2026-07-23 15:22:10+00"
	}
}
			

Lead Not Found (404):

{
	"success": false,
	"api_version": "1.0",
	"error": "Lead not found",
	"code": "NOT_FOUND"
}
			

You can only look up leads that belong to your own account.

Add Note Endpoint

POST https://localedgemarketingco.com/api?action=partner_add_note

Record activity against a lead you previously sent, for example "customer replied on our platform" or "showing booked". This is append only: it adds to the lead timeline and never changes existing lead data.

Request Body: note plus any one of lead_id, email or phone.

{
	"lead_id": "uuid-of-the-lead",
	"note": "Customer replied on our platform and asked for a showing."
}
			

Sample Success Response:

{
	"success": true,
	"api_version": "1.0",
	"lead_id": "uuid-or-id-here",
	"note_id": 390878
}
			
  • Notes are limited to 10,000 characters.
  • Returns 404 NOT_FOUND if the lead does not exist on your account.
List Leads Endpoint

POST https://localedgemarketingco.com/api?action=partner_list_leads

Pull your leads for reconciliation, or sync incrementally by passing updated_since. Results are ordered by most recently changed first, and only ever include leads on your own account.

Request Body: all fields optional.

{
	"updated_since": "2026-07-01T00:00:00Z",
	"stage": "New",
	"limit": 50,
	"offset": 0
}
			
  • updated_since: any parseable date or timestamp. Returns only leads changed at or after it.
  • stage: optional stage filter (case insensitive).
  • limit: defaults to 50, maximum 200.
  • offset: defaults to 0. Page forward while has_more is true.

Sample Success Response:

{
	"success": true,
	"api_version": "1.0",
	"leads": [
		{
			"lead_id": "uuid-or-id-here",
			"name": "Jane Smith",
			"stage": "Contacted",
			"type": "Buyer",
			"source": "Zillow",
			"tags": ["zillow", "buyer"],
			"created_at": "2026-07-23 14:07:57+00",
			"updated_at": "2026-07-23 15:22:10+00"
		}
	],
	"count": 1,
	"has_more": false,
	"limit": 50,
	"offset": 0
}
			
Update Lead Endpoint

POST https://localedgemarketingco.com/api?action=partner_update_lead

Update a lead you previously sent as it moves along your pipeline. Whitelisted to stage and tags only: the lead's name, source and other curated fields are never changed. Tags are additive (add with tags, remove with remove_tags); stage is set directly.

Request Body: any one of lead_id, email or phone, plus at least one of stage, tags or remove_tags.

{
	"lead_id": "uuid-of-the-lead",
	"stage": "Contacted",
	"tags": ["engaged", "hot"],
	"remove_tags": ["cold"]
}
			

Sample Success Response:

{
	"success": true,
	"api_version": "1.0",
	"lead": {
		"lead_id": "uuid-or-id-here",
		"name": "Jane Smith",
		"stage": "Contacted",
		"type": "Buyer",
		"source": "Zillow",
		"tags": ["engaged", "hot"],
		"created_at": "2026-07-23 14:07:57+00",
		"updated_at": "2026-07-28 09:15:00+00"
	}
}
			

Returns 400 MISSING_REQUIRED_FIELD if no updatable field is provided, or 404 NOT_FOUND if the lead is not on your account.

List Lead Notes Endpoint

POST https://localedgemarketingco.com/api?action=partner_list_lead_notes

Read the timeline notes on a lead you sent, newest first. Internal system notes are not included. This is the read counterpart to the add-note endpoint.

Request Body: any one of lead_id, email or phone, plus optional paging.

{
	"lead_id": "uuid-of-the-lead",
	"limit": 50,
	"offset": 0
}
			
  • limit: defaults to 50, maximum 200.
  • offset: defaults to 0. Page forward while has_more is true.

Sample Success Response:

{
	"success": true,
	"api_version": "1.0",
	"notes": [
		{
			"note_id": 390878,
			"message": "Customer replied on our platform and asked for a showing.",
			"type": "note",
			"created_at": "2026-07-24 10:30:00+00"
		}
	],
	"count": 1,
	"has_more": false,
	"limit": 50,
	"offset": 0
}
			
Opt Out Endpoint

POST https://localedgemarketingco.com/api?action=partner_opt_out_lead

Honor a do-not-contact request from your side. This suppresses the lead's phone and/or email so the account stops contacting them by SMS and email. It does not change the lead record.

Request Body: any one of lead_id, email or phone, plus an optional channel.

{
	"lead_id": "uuid-of-the-lead",
	"channel": "all"
}
			
  • channel: one of all (default), phone, or email.

Sample Success Response: suppressed reports how many phone and email identifiers are now suppressed for this lead.

{
	"success": true,
	"api_version": "1.0",
	"lead_id": "uuid-or-id-here",
	"suppressed": {
		"phone": 1,
		"email": 1
	}
}
			
Webhooks: Overview

Instead of polling, subscribe to events and have this CRM POST them to your server in real time. When a lead you own is created, changes stage, or gets a note, we send a signed JSON request to your registered URL. Webhooks use the same Bearer API key as the rest of the API.

  • Register a URL and the events you want; we return a signing secret (shown once).
  • We POST each event to your URL, signed with X-Webhook-Signature so you can verify it came from us.
  • Failed deliveries are retried with backoff. Delivery is at-least-once, so de-duplicate on X-Webhook-Delivery.
  • Your URL must be https and publicly reachable (private and reserved addresses are rejected).

POST https://localedgemarketingco.com/api?action=partner_register_webhook

Request Body: url plus events (any of lead.created, lead.stage_changed, note.added).

{
	"url": "https://your-server.com/webhooks/localedge",
	"events": ["lead.created", "lead.stage_changed", "note.added"]
}
			

Sample Success Response (the secret is shown only here, so save it):

{
	"success": true,
	"api_version": "1.0",
	"webhook_id": "uuid-here",
	"secret": "64-hex-character-signing-secret",
	"url": "https://your-server.com/webhooks/localedge",
	"events": ["lead.created", "lead.stage_changed", "note.added"]
}
			
Managing Webhooks

List: POST https://localedgemarketingco.com/api?action=partner_list_webhooks (no body). Returns your subscriptions; the secret is never returned again.

{
	"success": true,
	"api_version": "1.0",
	"webhooks": [
		{ "webhook_id": "uuid-here", "url": "https://your-server.com/webhooks/localedge", "events": ["lead.created"], "active": true, "created_at": "2026-07-29 17:00:00" }
	],
	"count": 1
}
			

Delete: POST https://localedgemarketingco.com/api?action=partner_delete_webhook with { "webhook_id": "uuid-here" }. Returns 404 NOT_FOUND if it is not yours.

Test: POST https://localedgemarketingco.com/api?action=partner_test_webhook with { "webhook_id": "uuid-here" }. We immediately send a signed webhook.test ping to your URL and report the HTTP status your endpoint returned, so you can confirm your receiver and signature check work.

{
	"success": true,
	"api_version": "1.0",
	"webhook_id": "uuid-here",
	"delivered": true,
	"status_code": 200,
	"error": null
}
			
Webhook Payloads

Every delivery is a POST with a JSON body and these headers:

  • X-Webhook-Event: the event type (for example lead.created).
  • X-Webhook-Delivery: a unique delivery id. Use it to de-duplicate retries.
  • X-Webhook-Signature: sha256=<hmac> (see Verifying the Signature below).

lead.created and lead.stage_changed (the latter also includes previous_stage):

{
	"api_version": "1.0",
	"event": "lead.stage_changed",
	"lead_id": "uuid-here",
	"name": "Jane Smith",
	"stage": "Contacted",
	"previous_stage": "New",
	"type": "Buyer",
	"source": "Zillow",
	"occurred_at": "2026-07-29T17:37:25+00:00"
}
			

note.added:

{
	"api_version": "1.0",
	"event": "note.added",
	"lead_id": "uuid-here",
	"note_id": 390878,
	"message": "Customer replied and asked for a showing.",
	"note_type": "note",
	"occurred_at": "2026-07-29T17:37:25+00:00"
}
			
Verifying the Signature

Every delivery is signed so you can confirm it came from us and was not altered. The X-Webhook-Signature header is sha256= followed by the HMAC-SHA256 of the raw request body, keyed with your webhook secret. Recompute it and compare using a constant-time check.

// Node.js
const crypto = require("crypto");
const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(rawBody).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.header("X-Webhook-Signature")));
			
# PHP
$expected = "sha256=" . hash_hmac("sha256", $rawBody, $secret);
$ok = hash_equals($expected, $_SERVER["HTTP_X_WEBHOOK_SIGNATURE"]);
			

Reject any request whose signature does not match. Always hash the exact bytes you received, before any JSON parsing or reformatting.

Delivery, Retries & Idempotency
  • Respond with a 2xx status to acknowledge. Anything else, or a timeout, is treated as a failure.
  • Failed deliveries retry with backoff: after 1 minute, then 5 minutes, 30 minutes, 2 hours, and 12 hours. After 5 failed attempts the delivery is dropped.
  • Respond quickly (within 10 seconds). Do heavy work asynchronously after you acknowledge.
  • Delivery is at-least-once: the same event can arrive more than once. De-duplicate using X-Webhook-Delivery.
  • Deliveries are independent and may arrive slightly out of order; use occurred_at if ordering matters.
  • Deactivating or deleting a webhook stops future deliveries.
Notes & Best Practices
  • If a lead with the same email or phone already exists, your data is merged into that lead and action comes back as updated. New emails, phones, tags and addresses are added, while the existing name, stage, type and source are preserved so your import never overwrites work the agent has already done.
  • Never share your API key publicly.
  • Log and monitor incoming requests for abuse or errors.
  • Test with Postman or cURL before going live with a partner.
  • If a partner can only send data by email or in a different format, use Zapier/Make or a custom script to reformat and forward to this API.
  • Address type is optional. If not provided, it will default to OTHER.
Email-to-Lead Parsing

You can also create leads by sending an email to your CRM's lead inbox. The email parser supports the same fields as the API, using simple text patterns in the email body.

  • Supported fields: name, stage, type, tags, source, addresses (with type, street, city, state, zip), phones, emails, notes, etc.
  • Addresses: Use lines like Address (HOME): 123 Main St, Springfield, IL, 62701. The type is optional and will default to OTHER if not provided.
  • Phones/Emails: Use Phone:, Phone 2:, Email:, Email 2:, etc. for multiple values.
  • Notes: Use Note:, Note 2:, etc. for multiple notes.
  • Tags: Use Tags: tag1, tag2, tag3.

Sample Email Body:

Name: Jane Smith
Stage: prospect
Type: seller
Tags: new, test, emailparser
Source: website

Address: 100 Main St, Springfield, IL, 62701
Address 2: 200 Oak Ave, Shelbyville, IL, 62565

Phone: 5551230001
Phone 2: 5551230002
Phone 3: 5551230003

Email: janesmith001@example.net
Email 2: contact.jane.smith@fakemail.org
Email 3: jsmith.random@nowhere.test

Note: This is a test note for Jane Smith.
Note 2: Second note for testing.
Note 3: Final test note.

user: {user email}
				

All fields are optional except name and at least one of email or phone. The parser will extract as much information as possible and send it to the API automatically.