Back to The Times of Claw

DenchClaw API Reference: Build Custom Integrations

DenchClaw REST API reference: authentication, entries CRUD, objects, fields, webhooks, documents, the Bridge API for Dench Apps, and curl examples for every endpoint.

Mark Rachapoom
Mark Rachapoom
·8 min read
DenchClaw API Reference: Build Custom Integrations

DenchClaw API Reference: Build Custom Integrations

DenchClaw exposes a REST API that lets you build custom integrations, automate workflows, and connect external tools without touching the DuckDB file directly. Whether you're building a custom form that creates CRM entries, a dashboard that pulls deal data, or a script that enriches contacts, the REST API is your interface.

This reference covers every endpoint category with working curl examples, authentication, error handling, and when to use the API versus direct DuckDB access.

Overview#

The DenchClaw API runs locally at http://localhost:4242 by default (port is configurable). All endpoints return JSON. Authentication uses API keys generated by the CLI.

Base URL: http://localhost:4242/api/v1

For remote access (via your DenchClaw instance URL):

Base URL: https://your-instance.dench.com/api/v1

Authentication#

Generate an API key#

npx denchclaw api-key create --name "My Integration" --permissions read,write

This outputs:

{
  "key": "dench_live_abc123xyz...",
  "name": "My Integration",
  "permissions": ["read", "write"],
  "created_at": "2026-03-26T18:00:00Z"
}

Store this key securely. It won't be shown again.

Authenticate requests#

Pass the API key in the Authorization header:

curl -H "Authorization: Bearer dench_live_abc123xyz..." \
  http://localhost:4242/api/v1/objects

Permission levels#

PermissionDescription
readRead entries, fields, objects, documents
writeCreate and update entries
deleteDelete entries
adminFull access including webhooks and field management

Core Endpoints#

Objects#

List all CRM objects (people, companies, deals, etc.):

# List all objects
curl -H "Authorization: Bearer $API_KEY" \
  http://localhost:4242/api/v1/objects
 
# Response
{
  "objects": [
    {
      "id": "obj_01",
      "name": "people",
      "label": "People",
      "field_count": 12,
      "entry_count": 847
    },
    {
      "id": "obj_02",
      "name": "companies",
      "label": "Companies",
      "field_count": 8,
      "entry_count": 203
    },
    {
      "id": "obj_03",
      "name": "deals",
      "label": "Deals",
      "field_count": 10,
      "entry_count": 156
    }
  ]
}

Fields#

Get fields for an object:

# List all fields on the people object
curl -H "Authorization: Bearer $API_KEY" \
  http://localhost:4242/api/v1/objects/people/fields
 
# Response
{
  "fields": [
    {
      "id": "fld_01",
      "name": "name",
      "label": "Name",
      "type": "text",
      "required": true
    },
    {
      "id": "fld_02",
      "name": "email",
      "label": "Email",
      "type": "email",
      "required": false
    },
    {
      "id": "fld_03",
      "name": "status",
      "label": "Status",
      "type": "select",
      "options": ["Lead", "Qualified", "Customer", "Churned"]
    }
  ]
}

Create a new field:

curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "lead_score",
    "label": "Lead Score",
    "type": "number"
  }' \
  http://localhost:4242/api/v1/objects/people/fields

Entries (CRUD)#

List entries#

# Get all people entries (paginated)
curl -H "Authorization: Bearer $API_KEY" \
  "http://localhost:4242/api/v1/objects/people/entries?limit=50&offset=0"
 
# Response
{
  "entries": [
    {
      "id": "entry_abc123",
      "object": "people",
      "created_at": "2026-01-15T09:00:00Z",
      "updated_at": "2026-03-10T14:30:00Z",
      "fields": {
        "name": "Sarah Chen",
        "email": "sarah@acme.com",
        "company_name": "Acme Corp",
        "status": "Customer",
        "phone": "+1-555-0123"
      }
    }
  ],
  "total": 847,
  "limit": 50,
  "offset": 0
}

Filter entries#

# Get all leads
curl -H "Authorization: Bearer $API_KEY" \
  "http://localhost:4242/api/v1/objects/people/entries?filter[status]=Lead"
 
# Get entries created after a date
curl -H "Authorization: Bearer $API_KEY" \
  "http://localhost:4242/api/v1/objects/people/entries?filter[created_after]=2026-01-01"
 
# Search by name
curl -H "Authorization: Bearer $API_KEY" \
  "http://localhost:4242/api/v1/objects/people/entries?search=Sarah"

Get a single entry#

curl -H "Authorization: Bearer $API_KEY" \
  http://localhost:4242/api/v1/objects/people/entries/entry_abc123
 
# Response
{
  "id": "entry_abc123",
  "object": "people",
  "created_at": "2026-01-15T09:00:00Z",
  "fields": {
    "name": "Sarah Chen",
    "email": "sarah@acme.com",
    "company_name": "Acme Corp",
    "status": "Customer"
  }
}

Create an entry#

curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "name": "Marcus Webb",
      "email": "marcus@startup.io",
      "company_name": "StartupCo",
      "status": "Lead",
      "phone": "+1-555-9876",
      "lead_source": "Conference"
    }
  }' \
  http://localhost:4242/api/v1/objects/people/entries
 
# Response
{
  "id": "entry_new456",
  "object": "people",
  "created_at": "2026-03-26T18:00:00Z",
  "fields": { ... }
}

Update an entry#

# Update specific fields (PATCH — partial update)
curl -X PATCH \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "status": "Qualified",
      "last_contacted": "2026-03-26"
    }
  }' \
  http://localhost:4242/api/v1/objects/people/entries/entry_abc123

Delete an entry#

curl -X DELETE \
  -H "Authorization: Bearer $API_KEY" \
  http://localhost:4242/api/v1/objects/people/entries/entry_abc123
 
# Response
{
  "deleted": true,
  "id": "entry_abc123"
}

Documents#

Documents are free-text markdown files linked to CRM entries.

# List documents for an entry
curl -H "Authorization: Bearer $API_KEY" \
  http://localhost:4242/api/v1/objects/people/entries/entry_abc123/documents
 
# Create a document (note) on an entry
curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Call Notes - March 26",
    "content": "# Call Notes\n\nDiscussed pricing. Sarah is interested in the Pro plan...",
    "category": "call_notes"
  }' \
  http://localhost:4242/api/v1/objects/people/entries/entry_abc123/documents
 
# Get a document
curl -H "Authorization: Bearer $API_KEY" \
  http://localhost:4242/api/v1/documents/doc_xyz789

Webhooks#

Manage webhooks via the API:

# List all webhooks
curl -H "Authorization: Bearer $API_KEY" \
  http://localhost:4242/api/v1/webhooks
 
# Create an outgoing webhook
curl -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "outgoing",
    "object": "deals",
    "event": "entry.updated",
    "url": "https://hooks.slack.com/services/YOUR/WEBHOOK",
    "name": "Deal Update Slack Alert",
    "secret": "your-secret-token"
  }' \
  http://localhost:4242/api/v1/webhooks
 
# Delete a webhook
curl -X DELETE \
  -H "Authorization: Bearer $API_KEY" \
  http://localhost:4242/api/v1/webhooks/wh_123

Request/Response Format#

All requests use JSON. All responses return JSON.

Standard response envelope#

{
  "data": { ... },
  "meta": {
    "request_id": "req_abc123",
    "timestamp": "2026-03-26T18:00:00Z"
  }
}

Pagination#

{
  "entries": [ ... ],
  "pagination": {
    "total": 847,
    "limit": 50,
    "offset": 100,
    "has_more": true
  }
}

Error Handling#

DenchClaw uses standard HTTP status codes:

StatusMeaning
200Success
201Created
400Bad Request (invalid payload)
401Unauthorized (invalid API key)
403Forbidden (insufficient permissions)
404Not Found
409Conflict (duplicate entry)
429Rate Limited
500Internal Server Error

Error response format:

{
  "error": {
    "code": "FIELD_NOT_FOUND",
    "message": "Field 'xyz' does not exist on object 'people'",
    "details": {
      "field": "xyz",
      "object": "people"
    }
  }
}

Rate Limits#

The local DenchClaw API has no rate limits (it's your own machine). For hosted/remote DenchClaw instances, rate limits apply:

  • 1,000 requests/minute for read operations
  • 100 requests/minute for write operations
  • Rate limit headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset

When rate limited, retry after the X-RateLimit-Reset timestamp.

The Bridge API (window.dench for Dench Apps)#

Dench Apps run inside DenchClaw's web UI and have access to window.dench, a JavaScript API that communicates with the platform without HTTP:

// In a Dench App (runs in the app's iframe context)
 
// Get current user/workspace info
const workspace = await window.dench.getWorkspace();
 
// Query entries
const leads = await window.dench.query({
  object: 'people',
  filter: { status: 'Lead' },
  limit: 100
});
 
// Create an entry
const newEntry = await window.dench.createEntry('people', {
  name: 'New Lead',
  email: 'lead@example.com',
  status: 'Lead'
});
 
// Update an entry
await window.dench.updateEntry('entry_abc123', {
  status: 'Qualified'
});
 
// Run a raw SQL query (read-only)
const results = await window.dench.sql(
  'SELECT status, COUNT(*) as count FROM v_people GROUP BY status'
);
 
// Show a notification
window.dench.notify('Entry updated successfully', 'success');

The Bridge API is only available inside Dench App iframes — it's not accessible from external domains.

REST API vs Direct DuckDB Access#

Use CaseREST APIDuckDB Direct
External integrations (forms, webhooks, 3rd party tools)
Custom Dench AppsUse Bridge APIFor complex queries
Python/Node.js scripts on the same machineEither✅ Faster
Remote access❌ (not over network)
Complex multi-join queries⚠️ (limited SQL support)
Real-time data access
Bulk data operations (thousands of rows)⚠️ (batching needed)✅ Much faster

For scripts running on the same machine as DenchClaw, direct DuckDB access is faster and more flexible. Use the REST API when the integration runs externally or needs to be portable.

SDK Availability#

DenchClaw doesn't have official SDKs yet, but the REST API is straightforward to wrap in any language.

JavaScript/TypeScript (unofficial):

npm install denchclaw-js  # community package

Python (unofficial):

pip install denchclaw  # community package

For the official SDK roadmap, check github.com/DenchHQ/denchclaw.

FAQ#

Does the API work when DenchClaw is offline? The REST API requires the DenchClaw server to be running (npx denchclaw start). Direct DuckDB access works regardless.

Can I use the API for external webhooks without an API key? Incoming webhooks have their own secret token system separate from the API. External tools post to webhook URLs authenticated by the webhook secret, not an API key.

Is there a GraphQL API? Not currently. The REST API is the primary external interface. For complex queries, DuckDB SQL is more powerful anyway.

How do I find an entry's ID? Query the entries endpoint with a search filter, or use DuckDB: SELECT id, name FROM v_people WHERE email = 'sarah@example.com'.

Can the API access documents across all entries? Yes. The /api/v1/documents endpoint lists all documents. Filter by linked_entry_id or category query parameters.

Ready to try DenchClaw? Install in one command: npx denchclaw. Full setup guide →

Mark Rachapoom

Written by

Mark Rachapoom

Building the future of AI CRM software.

Continue reading

DENCH

© 2026 DenchHQ · San Francisco, CA