API Keys Guide
Use team API keys to call Last Chance from scripts, automation, or other machine clients without a browser session.
Overview
API keys authenticate as your team, not as an individual user. Send the key on every request:
Authorization: Bearer YOUR_API_KEY
Keys are created in Account Settings → API Keys. The full secret is shown once at creation — store it securely (password manager, secrets vault, or environment variable). You can revoke a key at any time; revoked keys stop working immediately and remain visible for audit.
Who can manage keys: Team admins only.
Rate limits (API keys only): Dashboard/session traffic is unaffected.
| Bucket | Limit | Applies to |
|---|---|---|
| Auth attempts | 60 / minute per IP | Any lc_live_… Bearer (valid or invalid) |
| Global | 30 requests / minute per key | Dual-auth API routes (including tool-backed MCP calls); not the /mcp entry handshake itself |
| MCP entry | 60 / minute per key | Remote MCP endpoint (/mcp) only — separate from the global bucket |
| Analytics summary | 20 / minute per key | /api/dashboard/stats, /api/analytics/unsubscribe-rates |
| Analytics attribution | 6 / minute per key | /api/analytics/revenue-impact |
AI endpoints keep their existing tighter limits. Exceeding a limit returns 429.
MCP throughput: Each MCP request counts 1× against MCP entry and 1× against the per-IP auth-attempt bucket at /mcp. Each tools/call then counts 1× against global (plus analytics caps when those tools run) — in-process tool handlers do not re-charge auth-attempt. The practical ceiling is about 30 tool calls per minute per key (the global bucket), even though MCP entry allows 60.
Create a key
- Open Account Settings → API Keys
- Click Create key
- Choose a name (for example
CI scriptsorBilling sync) - Pick scopes (defaults are read-only:
pages:readandteam:read) - Choose expiry (defaults to 90 days; you can pick 30 / 90 / 365 days or never)
- Copy the secret immediately — it will not be shown again
Limits:
- Up to 20 active keys per team
- Create rate limit: 10 keys per hour per team
Connect with MCP (Claude / Cursor)
Use a team API key with Last Chance’s hosted MCP server so Claude or Cursor can list pages, check integrations, and pull analytics—without cloning any repo.
Primary app host only. Use https://www.yourlastchance.app/mcp. Branded unsubscribe hosts (for example your-brand.yourlastchance.app) do not serve MCP — those hosts only expose public unsubscribe paths.
- Create a key with at least one of
pages:read,team:read,analytics:summary:read, oranalytics:attribution:read(add the scopes for the tools you need). - In Account Settings → API Keys, click Copy MCP config (template uses
YOUR_API_KEY), or Copy MCP config in the “Save your API key” dialog after creating a key (fills in thelc_live_…secret). - Paste into Cursor (
~/.cursor/mcp.jsonor project.cursor/mcp.json) or Claude Desktop / Claude Code remote MCP settings. ReplaceYOUR_API_KEYif you copied the template.
Example config
{
"mcpServers": {
"lastchance": {
"url": "https://www.yourlastchance.app/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}
Prefer an env var in Cursor when you manage secrets outside the file:
{
"mcpServers": {
"lastchance": {
"url": "https://www.yourlastchance.app/mcp",
"headers": {
"Authorization": "Bearer ${env:LAST_CHANCE_API_KEY}"
}
}
}
}
Set LAST_CHANCE_API_KEY in your environment to your lc_live_… secret (or paste the Bearer value directly — prefer the env var so the key is not committed).
Available tools (read-only)
get_team_context— team tier, limits, Klaviyo/Shopify flagslist_email_integrations/list_klaviyo_lists— integrations and listslist_unsubscribe_pages/get_unsubscribe_page/list_page_versions— page discoveryget_unsubscribe_page_urls— preview / draft / share URLs (prefershareUrlfor stakeholders)get_dashboard_stats/get_unsubscribe_rates— summary analytics (analytics:summary:read)get_revenue_impact— attribution analytics (analytics:attribution:read; requiresstartDate+endDate)
Revoke the key in Account Settings → API Keys to cut off access immediately.
Authentication
Every machine request needs your API key in the Authorization header:
-H "Authorization: Bearer YOUR_API_KEY"
-H "Content-Type: application/json"
Replace YOUR_API_KEY with the full secret from Account Settings → API Keys. All examples below use https://www.yourlastchance.app.
Cookie/session auth still works for the dashboard. Non-Last Chance Bearer headers are ignored so browser sessions keep working.
Scopes
| Scope | Allows |
|---|---|
pages:read | List and get unsubscribe pages and versions |
pages:write | Create pages, save drafts, archive/primary, attach/detach email lists, toggle list webhooks, delete pages (with confirm) |
pages:publish | Publish a draft version |
pages:ai | AI page chat and generate-from-website |
team:read | Team context/limits, email integrations status, Klaviyo list discovery |
analytics:summary:read | Dashboard stats and unsubscribe rate trends (aggregated counts only — no email PII) |
analytics:attribution:read | Revenue impact / churn recovery attribution for a date range |
Grant the minimum scopes your client needs. A read-only integration usually only needs pages:read and team:read. Add analytics:summary:read / analytics:attribution:read when the client should pull metrics.
Example requests
Get team context
Requires team:read.
curl -sS "https://www.yourlastchance.app/api/team" \
-H "Authorization: Bearer YOUR_API_KEY"
Returns team id/name, tier, Klaviyo/Shopify connection flags, subscription/access booleans, and page/AI/analytics limits. API-key responses omit billing provider IDs, shop domain, and other dashboard-only fields.
List email integrations
Requires team:read.
curl -sS "https://www.yourlastchance.app/api/email-integrations" \
-H "Authorization: Bearer YOUR_API_KEY"
List Klaviyo lists
Requires team:read. Uses the team bound to the API key (do not pass another team’s teamId).
curl -sS "https://www.yourlastchance.app/api/email-integrations/klaviyo/lists" \
-H "Authorization: Bearer YOUR_API_KEY"
List unsubscribe pages
Requires pages:read.
curl -sS "https://www.yourlastchance.app/api/unsubscribe-pages" \
-H "Authorization: Bearer YOUR_API_KEY"
Get one page
Requires pages:read.
curl -sS "https://www.yourlastchance.app/api/unsubscribe-pages/PAGE_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
Get preview, draft, and share URLs
Requires pages:read. Returns absolute URLs for the page (uses your branded public host when configured). Optional versionNumber and experienceKey query params.
| Field | Use for |
|---|---|
shareUrl | Sending a link to stakeholders (Slack, email, etc.). Public — no login and no email required. Prefer this for external review. |
previewUrl | You testing the published page (same flow as dashboard Preview). |
draftUrl | You testing a newer unpublished version. null when the latest version is already published. |
Do not send previewUrl or draftUrl to external reviewers; use shareUrl instead.
curl -sS "https://www.yourlastchance.app/api/unsubscribe-pages/PAGE_ID/urls" \
-H "Authorization: Bearer YOUR_API_KEY"
Create a page from a template
Requires pages:write. Unpaid teams are limited to 3 pages total; paid teams are unlimited.
curl -sS -X POST "https://www.yourlastchance.app/api/unsubscribe-pages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Stay offer page",
"templateId": "TEMPLATE_ID"
}'
Save a draft version
Requires pages:write.
curl -sS -X POST "https://www.yourlastchance.app/api/unsubscribe-pages/PAGE_ID/versions" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Draft from API",
"versionNumber": 2,
"globalStyles": ":root { --primary: #111; }",
"pages": [
{
"id": "initial",
"name": "Initial",
"html": "<div data-action=\"stay\">Stay subscribed</div>"
}
]
}'
Required fields: name, versionNumber, globalStyles, and pages (an array of page objects). Use the same JSON shape the dashboard editor saves (HTML/CSS, theme, offers). Unchanged content may return the existing version instead of creating a new one. In practice, fetch the current version first, modify it, then POST.
Publish a version
Requires pages:publish. Publishing requires an active subscription.
curl -sS -X POST "https://www.yourlastchance.app/api/unsubscribe-pages/PAGE_ID/versions/VERSION_ID/publish" \
-H "Authorization: Bearer YOUR_API_KEY"
Attach a Klaviyo list to a page
Requires pages:write.
curl -sS -X POST "https://www.yourlastchance.app/api/unsubscribe-pages/PAGE_ID/email-lists" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"listId": "KLAVIYO_LIST_ID",
"listName": "Newsletter",
"provider": "klaviyo"
}'
Delete a page (API key only)
Requires pages:write and confirm=true (query or JSON body). Session deletes from the dashboard do not need this flag.
curl -sS -X DELETE "https://www.yourlastchance.app/api/unsubscribe-pages/PAGE_ID?confirm=true" \
-H "Authorization: Bearer YOUR_API_KEY"
Or:
curl -sS -X DELETE "https://www.yourlastchance.app/api/unsubscribe-pages/PAGE_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"confirm": true}'
Dashboard stats (summary)
Requires analytics:summary:read. Returns aggregated stay/unsubscribe counts, visitor retention, and attributed order revenue for the team (no per-email data).
curl -sS "https://www.yourlastchance.app/api/dashboard/stats" \
-H "Authorization: Bearer YOUR_API_KEY"
Unsubscribe rates (summary)
Requires analytics:summary:read. Compares Klaviyo unsubscribe rates before/after Last Chance activation. Optional startDate / endDate (ISO). Requires an active Klaviyo integration.
curl -sS "https://www.yourlastchance.app/api/analytics/unsubscribe-rates" \
-H "Authorization: Bearer YOUR_API_KEY"
Revenue impact (attribution)
Requires analytics:attribution:read. Full-funnel churn recovery and revenue metrics. startDate and endDate are required (ISO date strings). Optional: enforceActivationDate, variantId, attributionWindowMode. Requires an active Klaviyo integration.
curl -sS "https://www.yourlastchance.app/api/analytics/revenue-impact?startDate=2026-01-01&endDate=2026-03-31" \
-H "Authorization: Bearer YOUR_API_KEY"
Event-level analytics that include email addresses are not available via API keys.
Common errors
| Status | Code / message | Meaning |
|---|---|---|
401 | Unauthorized | Missing/invalid key, revoked key, or expired key |
403 | insufficient_scope | Key lacks a required scope |
403 | Active subscription required | Publish (and some paid features) need a paid team |
400 | CONFIRMATION_REQUIRED | API-key page delete missing confirm=true |
403 | API_KEY_LIMIT_REACHED | Team already has 20 active keys |
429 | Rate limited | Too many API-key requests, AI calls, or key creates |
Responses never include Klaviyo OAuth secrets (accessToken, refreshToken, etc.).
Security best practices
- Prefer short-lived keys (90 days) and rotate before expiry
- Use the smallest scope set that works
- Store secrets in env vars or a vault — never commit them
- Revoke keys you no longer need; check Last used on the API Keys page
- Do not share one key across unrelated systems if you can avoid it
Related
- Settings — Other account configuration
- Getting Started — Create and publish your first page in the UI
- Manage keys in-app: Account Settings → API Keys