Skip to main content

DraftLift API

The DraftLift API gives you programmatic access to content generation, memories, templates, and content management. Available on Business and Agency plans.

Base URL

https://draftliftai.com/api/v1

Authentication

All requests require a Bearer token. Create an API key from Settings > API Keys in your dashboard.
curl -H "Authorization: Bearer dl_live_your_key_here" \
  https://draftliftai.com/api/v1/templates
See Authentication for details on creating and managing API keys.

Scopes

Every API key carries a set of scopes that control which operations it can perform. Scopes are picked when the key is created (in Settings > API Keys) and cannot be changed afterward — re-issue a new key to change scopes.
ScopeGrants
readList and fetch resources (templates, memories, contents, references, voice profiles, usage). Always present.
writeCreate, update, and delete resources. Required for generate, repurpose, score, and any POST/PUT/PATCH/DELETE endpoint.
Defaults. New keys are issued with ['read', 'write']. Uncheck Write at creation time to mint a read-only key — useful for analytics dashboards or audit tools that should never mutate data. Errors. Calling a write endpoint with a read-only key returns 403 with a structured detail body:
{
  "error_code": "FORBIDDEN",
  "details": {
    "error_code": "insufficient_scope",
    "message": "This endpoint requires the 'write' scope.",
    "required_scope": "write",
    "current_scopes": ["read"],
    "upgrade_action": "Re-issue your API key at https://app.draftliftai.com/settings/api-keys"
  }
}
The CLI surfaces this as InsufficientScopeError (exit code 2) with the upgrade URL on its own line.

Workspace binding

API keys can optionally be bound to a single workspace at creation time. Binding scopes the key to that workspace for any workspace-aware operation (references, contents, conversions, knowledge graph, workspace listing).
Key typeWorkspace resolution
Bound (one workspace)All workspace-scoped requests auto-target the bound workspace. Passing workspace_id is optional; passing a different workspace returns 403.
Unbound (all workspaces)Workspace-scoped requests must include workspace_id (body, query, or path). The CLI’s --workspace global flag plumbs this through.
Solo users (exactly one workspace) get their key auto-bound at creation — there’s nothing to disambiguate. Mismatch error. Calling an endpoint with a workspace_id that doesn’t match the key’s binding returns 403:
{
  "error_code": "FORBIDDEN",
  "details": {
    "error_code": "workspace_mismatch",
    "message": "This API key is bound to a specific workspace.",
    "bound_workspace_id": "ws_abc123",
    "requested_workspace_id": "ws_def456"
  }
}
Account-level reads (entitlements, usage, profile, voice profiles) are user-scoped, not workspace-scoped, and ignore binding.

What’s not in the API

A few surfaces are intentionally web-session-only and reject API keys with 401. These require interactive browser auth:
  • Billing mutationsPOST /billing/checkout-session, /billing/portal-session, /billing/cancel-subscription, /billing/refund-request. Stripe checkout and refund flows are interactive by design.
  • Account mutationsPOST /register, PUT /me/settings, PUT /learning-preferences. Account-level changes belong in the dashboard.
  • API key managementPOST/PUT/DELETE /api-keys. Managing keys with a key would be a bootstrap footgun. The one exception is GET /api-keys/current, which lets a key introspect its own metadata (used by dl whoami).
  • Admin routes — the entire /admin/* namespace stays JWT-only.
  • Workspace mutations — workspace create/update/delete is web-only; reads (GET /workspaces, GET /workspaces/:id) are available via API key.
If you have a use case that needs one of these surfaces from a headless context, let us know — we’d rather understand the workflow than expose the endpoint by default.

v1 stability commitment

The /api/v1 surface follows strict semver:
  • Additive changes only. New endpoints, new optional request fields, and new response fields can ship without notice.
  • No breaking changes. Removing endpoints, removing response fields, renaming fields, tightening validation, or changing status codes for existing flows are breaking changes and will not happen on /api/v1.
  • Breaking changes ship under /api/v2. When v2 mounts, v1 continues to work for a documented deprecation window.
  • Stability tags. Endpoints are tagged stable or beta in the OpenAPI spec. Beta endpoints follow the same v1 contract but may iterate faster on response shape until promoted to stable. Internal-only endpoints (CLI device-flow auth, dev tools) are not documented and not part of the v1 contract.

Quickstart: generate in one call

The generate endpoint is the core of the API. Pass a template and your direction — DraftLift handles everything else.

1. Get your API key

Create one at Settings > API Keys. Copy it immediately — it’s only shown once.

2. Pick a template

curl -s https://draftliftai.com/api/v1/templates \
  -H "Authorization: Bearer dl_live_your_key_here"
Note the id of the template you want (e.g. a LinkedIn Post template).

3. Generate

curl -X POST https://draftliftai.com/api/v1/generate \
  -H "Authorization: Bearer dl_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": 1,
    "user_direction": "Write about why most B2B companies waste money on generic SEO content instead of opinion-driven thought leadership"
  }'
No content_id needed — the API creates one automatically and returns it in the response.

4. Review and finalize

After editing, finalize the content to trigger learning analysis:
curl -X POST https://draftliftai.com/api/v1/finalizeContent \
  -H "Authorization: Bearer dl_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "content_id": 789,
    "finalized_content": "Your edited final version here..."
  }'

Headless agent workflow

The API is designed for AI agents operating headlessly. A typical loop:
# 1. Generate
result = requests.post(f"{BASE}/generate", headers=headers, json={
    "template_id": TEMPLATE_ID,
    "user_direction": direction,
    "memory_ids": [1, 5],
}).json()

content_id = result["content_id"]
generated = result["generated_content"]

# 2. Review / edit (your agent logic here)
final_content = agent_review_and_edit(generated)

# 3. Finalize
requests.post(f"{BASE}/finalizeContent", headers=headers, json={
    "content_id": content_id,
    "finalized_content": final_content,
})
Three calls: generate, review in your environment, finalize. No content creation step needed.

Generate with context

Pass memory_ids to inject your voice and knowledge into the generation:
curl -X POST https://draftliftai.com/api/v1/generate \
  -H "Authorization: Bearer dl_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": 1,
    "user_direction": "Write a LinkedIn post about content-led growth",
    "memory_ids": [1, 5, 12]
  }'
You can also pass reference_material_ids for source material the AI should draw from.

Rate limits

PlanRequests/minRequests/hour
Business1001,000
Agency3005,000
Rate limit headers are included in every response:
  • X-RateLimit-Limit — Maximum requests per window
  • X-RateLimit-Remaining — Requests remaining
  • X-RateLimit-Reset — Unix timestamp when the window resets

Error handling

All errors return a detail field:
{
  "detail": "Description of the error"
}
StatusMeaning
401Missing or invalid API key
403Your plan doesn’t include API access
404Resource not found
422Validation error (check request body)
429Rate limit exceeded — see Retry-After header