LeadPanther API LLM Guide#

Status: initial public guide for current API v1 behavior. Last updated: 2026-08-11.

Purpose#

This guide is for LLMs, agents, and code-generation tools that help users call the LeadPanther API. Follow these rules exactly. Do not infer endpoints, hosts, scopes, or account behavior from common SaaS API patterns.

Non-Negotiable Rules#

  1. Use only this current API v1 base URL:
https://app.leadpanther.ai/api/v1
  1. Do not use this host as the app API base URL:
https://api.leadpanther.ai/v1
  1. Do not guess endpoint names. Use only documented paths under https://app.leadpanther.ai/api/v1.

  2. If the user says they are posting for a client, use:

/clients/{clientId}/posts

Do not use /posts for agency-client work.

  1. If the user says they are managing lead magnets for a client, use:
/clients/{clientId}/lead-magnets

Do not use /lead-magnets for agency-client work.

  1. If the user says they are reading leads or activity for a client, use:
/clients/{clientId}/leads
/clients/{clientId}/activity

Do not use direct /leads or /activity routes for agency-client work.

  1. If a call returns 403, check both:
  • the API key scope
  • the agency-client grant
  1. Do not use Supabase REST, Supabase storage URLs, service-role keys, key hashes, internal database IDs, or private operational docs unless the user explicitly asks for internal implementation work and has authorization.

  2. Never place API keys in browser code, frontend bundles, mobile apps, public repositories, generated docs, screenshots, or logs.

  3. Treat leads:read and activity:read as sensitive scopes. They can expose personal data, message/comment content, LinkedIn profile URLs, platform identifiers, and intent metadata.

  4. For LinkedIn-facing DMs, comments, reposts, and follow-ups, do not batch actions at the same timestamp or exact repeating intervals. Generate randomized due times, spread actions across working hours, and follow LinkedIn Safety.

  5. For list endpoints, use the canonical limit/offset loop: start at offset=0, keep filters stable, advance by the returned pagination.limit while pagination.has_more is true, and retry the same offset after a page-level error.

  6. Fast Response is temporary and post-specific: use only 2, 5, or 10 minute intervals and 30 or 60 minute durations. Defaults are 10 and 60. Never send provider account/post identifiers. Only one scheduled or active post can occupy a time window for each resolved client account. API writes are account-scoped preview operations and return 404 not_found when the resolved subject is not enabled; do not retry that response as a transient failure.

  7. Published LinkedIn edits and deletes are capability-driven. Fetch the post immediately before mutation and inspect published_mutation_capabilities; never infer support from status, a LinkedIn URL, or provider IDs. Published edits may change text only, not attached media. Send Idempotency-Key plus the latest capability etag in X-LeadPanther-Post-ETag for text edits, and Idempotency-Key for deletes.

  8. A published delete removes the LinkedIn post, its comments/reactions, LeadPanther tracking, and active automation. Confirm that destructive scope with the user before calling it. On 409 with error.details.reason=provider_state_refreshed, fetch and show the refreshed post and require review before retrying.

Current API Surface#

Use only these current API v1 routes.

TaskDirect-user pathAgency-client pathScope
API metadataGET /api/v1n/anone
Current API subjectGET /api/v1/men/avalid API key
List clientsGET /api/v1/clientsn/aclients:read
Get clientGET /api/v1/clients/{clientId}n/aclients:read
List postsGET /api/v1/postsGET /api/v1/clients/{clientId}/postsposts:read
Create postPOST /api/v1/postsPOST /api/v1/clients/{clientId}/postsposts:write
Get postGET /api/v1/posts/{id}GET /api/v1/clients/{clientId}/posts/{id}posts:read
Update postPATCH /api/v1/posts/{id}PATCH /api/v1/clients/{clientId}/posts/{id}posts:write
Delete postDELETE /api/v1/posts/{id}DELETE /api/v1/clients/{clientId}/posts/{id}posts:write
Get post engagementGET /api/v1/posts/{id}/engagementGET /api/v1/clients/{clientId}/posts/{id}/engagementanalytics:read
Start Fast ResponsePOST /api/v1/posts/{id}/fast-response/startPOST /api/v1/clients/{clientId}/posts/{id}/fast-response/startposts:write
Stop Fast ResponsePOST /api/v1/posts/{id}/fast-response/stopPOST /api/v1/clients/{clientId}/posts/{id}/fast-response/stopposts:write
List lead magnetsGET /api/v1/lead-magnetsGET /api/v1/clients/{clientId}/lead-magnetslead_magnets:read
Create lead magnetPOST /api/v1/lead-magnetsPOST /api/v1/clients/{clientId}/lead-magnetslead_magnets:write
Get lead magnetGET /api/v1/lead-magnets/{id}GET /api/v1/clients/{clientId}/lead-magnets/{id}lead_magnets:read
Update lead magnetPATCH /api/v1/lead-magnets/{id}PATCH /api/v1/clients/{clientId}/lead-magnets/{id}lead_magnets:write
Archive lead magnetDELETE /api/v1/lead-magnets/{id}DELETE /api/v1/clients/{clientId}/lead-magnets/{id}lead_magnets:write
Lead magnet analyticsGET /api/v1/lead-magnets/{id}/analyticsGET /api/v1/clients/{clientId}/lead-magnets/{id}/analyticsanalytics:read
Account defaults for new magnets (sequences, DM, reply, capture mode)GET /api/v1/account/playbook-defaultsGET /api/v1/clients/{clientId}/playbook-defaultslead_magnets:read
List magnet default presetsGET /api/v1/account/playbook-presetsGET /api/v1/clients/{clientId}/playbook-presetslead_magnets:read
Create magnet default presetPOST /api/v1/account/playbook-presetsPOST /api/v1/clients/{clientId}/playbook-presetslead_magnets:write
Get / update / delete presetGET, PATCH, DELETE /api/v1/account/playbook-presets/{presetId}same under /clients/{clientId}/playbook-presets/{presetId}lead_magnets:read / lead_magnets:write
Create media upload URLPOST /api/v1/media/upload-urlPOST /api/v1/clients/{clientId}/media/upload-urlmedia:write
Delete mediaDELETE /api/v1/mediaDELETE /api/v1/clients/{clientId}/mediamedia:write
List leadsGET /api/v1/leadsGET /api/v1/clients/{clientId}/leadsleads:read
List activityGET /api/v1/activityGET /api/v1/clients/{clientId}/activityactivity:read

When constructing a URL from the table, do not duplicate /api/v1. For example:

https://app.leadpanther.ai/api/v1/me
https://app.leadpanther.ai/api/v1/clients/00000000-0000-4000-8000-000000000001/posts

Unsupported Guessed Routes#

Do not generate calls to these routes unless future docs explicitly add them:

/accounts
/users
/lists
/campaigns
/organizations
/team
/workspaces
/lead-magnet-posts
/webhooks

The only documented workspace API v1 route is /workspaces/{workspaceId}/social-actions; do not generate generic workspace management calls from that route.

Unsupported guessed endpoints may return 404, method_not_allowed, or other invalid path behavior.

Account Model#

Direct-user routes operate on the API key owner's account.

Agency-client routes operate on a target client account and require an active agency-client grant:

/api/v1/clients/{clientId}/...

The public clientId currently refers to the client account user_id returned by GET /clients. Do not expose or request private implementation identifiers such as account table IDs, API key IDs, privileged backend data, key hashes, or production database IDs.

Use placeholder IDs in examples:

00000000-0000-4000-8000-000000000001
00000000-0000-4000-8000-000000000002
00000000-0000-4000-8000-000000000003

Authentication Pattern#

Use a server-side bearer token:

curl -sS \
  -H "Authorization: Bearer $LEADPANTHER_API_KEY" \
  "https://app.leadpanther.ai/api/v1/me"

Use environment variables in generated code:

LEADPANTHER_API_KEY=lp_live_REDACTED

Do not hard-code a key. Do not invent a key. Do not ask users to paste real keys into prompts.

Request ID Pattern#

For troubleshooting examples, include X-Request-Id:

curl -sS \
  -H "Authorization: Bearer $LEADPANTHER_API_KEY" \
  -H "X-Request-Id: req_example_001" \
  "https://app.leadpanther.ai/api/v1/me"

Tell users to provide the response request_id to LeadPanther support when reporting an API issue.

Response Envelopes#

Success responses use:

{
  "data": {},
  "request_id": "req_000000000000000000000000"
}

List responses use:

{
  "data": [],
  "pagination": {
    "limit": 50,
    "offset": 0,
    "has_more": false
  },
  "request_id": "req_000000000000000000000000"
}

Error responses use:

{
  "error": {
    "code": "bad_request",
    "message": "Invalid payload."
  },
  "request_id": "req_000000000000000000000000"
}

Do not assume field-level validation details are available. Current validation errors may be general.

Pagination Rules#

All list endpoints use limit and offset unless a specific endpoint documents otherwise. Default limit is generally 50; maximum is generally 100.

Canonical loop:

  1. Request ?limit=50&offset=0 unless the user needs a different documented limit.
  2. Keep the same query filters for the full export.
  3. Process data.
  4. If pagination.has_more is true, add pagination.limit to the previous offset.
  5. Stop when pagination.has_more is false.
  6. If a page fails, retry that exact page and report the response request_id; do not skip to the next offset.

Good:

GET /api/v1/leads?limit=50&offset=0

Bad:

GET /api/v1/leads?page=1&page_size=50

Do not invent cursor pagination.

Privacy and Redaction Rules#

Use anonymized values in all generated examples:

  • Client A
  • Example Agency
  • 00000000-0000-4000-8000-000000000001
  • 00000000-0000-4000-8000-000000000002
  • 00000000-0000-4000-8000-000000000003
  • person@example.com
  • REDACTED_MESSAGE_CONTENT
  • REDACTED_PLATFORM_MESSAGE_ID
  • REDACTED_SIGNED_UPLOAD_URL
  • REDACTED_UPLOAD_TOKEN

Never include:

  • real customer names
  • real customer emails
  • real API keys
  • real production UUIDs
  • real lead message text
  • real LinkedIn message IDs, chat IDs, or comment IDs
  • service-role keys
  • key hashes
  • Supabase REST examples for public API use

leads:read can return personal data. activity:read can return message/comment content and platform identifiers. Public examples are redacted and do not represent the full sensitivity of production data.

Safe Example: Direct User Lists Posts#

curl -sS \
  -H "Authorization: Bearer $LEADPANTHER_API_KEY" \
  "https://app.leadpanther.ai/api/v1/posts?limit=10&offset=0"

Required scope: posts:read.

Safe Example: Agency Lists Client Posts#

curl -sS \
  -H "Authorization: Bearer $LEADPANTHER_API_KEY" \
  "https://app.leadpanther.ai/api/v1/clients/00000000-0000-4000-8000-000000000001/posts?limit=10&offset=0"

Required scope: posts:read.

Also required: active agency-client grant for 00000000-0000-4000-8000-000000000001.

Safe Example: Agency Creates Client Post#

curl -sS \
  -X POST \
  -H "Authorization: Bearer $LEADPANTHER_API_KEY" \
  -H "Content-Type: application/json" \
  "https://app.leadpanther.ai/api/v1/clients/00000000-0000-4000-8000-000000000001/posts" \
  -d '{
    "text": "Public-safe LinkedIn post copy.",
    "status": "draft",
    "scheduled_at": null,
    "theme_id": null,
    "template_id": null,
    "images": [],
    "document_url": null,
    "notes": "Example internal note."
  }'

Required scope: posts:write.

Safe Example: Edit Published LinkedIn Text#

First fetch the post and verify published_mutation_capabilities.can_edit is true. Copy its opaque etag exactly; do not generate one. Keep the same idempotency key only when replaying this exact request.

curl -sS \
  -X PATCH \
  -H "Authorization: Bearer $LEADPANTHER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $LEADPANTHER_IDEMPOTENCY_KEY" \
  -H "X-LeadPanther-Post-ETag: $LEADPANTHER_POST_ETAG" \
  "https://app.leadpanther.ai/api/v1/posts/00000000-0000-4000-8000-000000000002" \
  -d '{"text":"Reviewed replacement LinkedIn post text."}'

Published text can be updated without changing existing attachments. Attached media itself cannot be added, replaced, or removed. On provider_state_refreshed or stale_provider_version, fetch the post again and require review of the current text before retrying.

Safe Example: Delete a Published LinkedIn Post#

Only generate this call after confirming that the user understands it removes the LinkedIn post and its comments/reactions, plus LeadPanther tracking and active automation.

curl -sS \
  -X DELETE \
  -H "Authorization: Bearer $LEADPANTHER_API_KEY" \
  -H "Idempotency-Key: $LEADPANTHER_IDEMPOTENCY_KEY" \
  "https://app.leadpanther.ai/api/v1/posts/00000000-0000-4000-8000-000000000002"

Fetch the post first and proceed only when published_mutation_capabilities.can_delete is true.

Safe Example: List Leads With Privacy Warning#

curl -sS \
  -H "Authorization: Bearer $LEADPANTHER_API_KEY" \
  "https://app.leadpanther.ai/api/v1/clients/00000000-0000-4000-8000-000000000001/leads?limit=50&offset=0"

Required scope: leads:read.

Warn the user that this response can contain personal data, including names, LinkedIn profile URLs, submitted emails, work emails, personal emails, sources, intent signals, qualification status, and timestamps.

Safe Example: List Activity With Privacy Warning#

curl -sS \
  -H "Authorization: Bearer $LEADPANTHER_API_KEY" \
  "https://app.leadpanther.ai/api/v1/clients/00000000-0000-4000-8000-000000000001/activity?limit=50&offset=0"

Required scope: activity:read.

Warn the user that this response can contain message/comment content, author information, LinkedIn profile URLs, source metadata, message IDs, chat IDs, comment IDs, and intent metadata.

Current vs Planned Boundaries#

Current:

  • API v1 base URL is https://app.leadpanther.ai/api/v1.
  • API keys are provisioned privately for now.
  • API keys are bearer tokens and must stay server-side.
  • Current list pagination is limit and offset.
  • Agency client work uses /clients/{clientId}/....
  • /lead-magnet-posts is not currently implemented.

Planned or not currently guaranteed:

  • Published rate limits are not yet a public contract.
  • Idempotency keys are documented only where explicitly specified, including published post mutations, Fast Response starts, and POST /workspaces/{workspaceId}/social-actions.
  • Webhook endpoints are planned, not currently available.
  • SDKs are planned or future, not currently available.
  • webhooks:write may appear as a scope, but no public v1 webhook route is currently implemented.
  • OpenAPI and generated SDK contracts may be added later, but do not invent them now.

Troubleshooting Decision Tree#

For 401 unauthorized:

  • Check the bearer token header.
  • Check that the key is active.
  • Keep the key server-side.

For 403 forbidden:

  • Check the endpoint's required scope.
  • If agency-client work is involved, check the active agency-client grant.
  • Confirm the route is nested under /clients/{clientId}/....

For 404 not_found:

  • Check the base URL.
  • Check the path against the documented endpoint list.
  • Do not switch to guessed routes.
  • For agency work, verify the client ID through an authorized workflow.

For 400 bad_request:

  • Check JSON syntax.
  • Check required fields.
  • Check query parameters.
  • Check current payload rules for posts, lead magnets, media, leads, or activity.

For 409 conflict:

  • Check resource state. Published or publishing posts have update and delete restrictions.

For 500 internal_error:

  • Retry later if appropriate.
  • Capture the response request_id.
  • Do not expose sensitive request or response data when reporting the issue.