CRM Block

Enterprise customer relationship management through a simple REST API
The CRM Block provides a complete customer relationship management solution. Manage unlimited contacts, track company accounts, build deal pipelines, log activities, and score leads - all through a RESTful API with real-time webhooks.
Key Features
- Contact Management - Unlimited contacts with custom fields, tags, and segmentation
- Company Accounts - Link contacts to companies with hierarchies and account scoring
- Deal Pipeline - Customizable stages, probability tracking, and revenue forecasting
- Activity Tracking - Calls, emails, meetings, and tasks with timeline views
- Lead Scoring - Behavioral scoring with custom rules and score decay
- Custom Fields - Define unlimited custom fields for contacts, companies, and deals
- Relationship Insights - Health scores, engagement analytics, and next-best-action
- Real-time Webhooks - Instant notifications for all record changes
API Endpoint
| Service | URL |
|---|---|
| CRM | https://crm.api.us.23blocks.com |
Environment Routing: One URL, one environment. All shared-platform API keys are live keys:
pk_live_*/sk_live_*→ your production tenant. Need a separate staging environment? It is available as a paid add-on on deployments in your own cloud account — see 23blocks in a Box.
Quick Start
Installation
npm install @23blocks/sdk
Basic Usage
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { crm: 'https://crm.api.us.23blocks.com' },
apiKey: 'your-api-key',
});
// Create a contact
const contact = await client.crm.contacts.create({
first_name: 'Jane',
last_name: 'Smith',
email: 'jane@company.com',
phone: '+1-555-0123',
tags: ['enterprise', 'decision-maker']
});
// Create a company and link the contact
const company = await client.crm.companies.create({
name: 'Acme Corp',
industry: 'Technology',
size: '100-500',
website: 'https://acme.com'
});
// Link contact to company
await client.crm.contacts.update(contact.id, {
company_id: company.id
});
// Create a deal
const deal = await client.crm.deals.create({
name: 'Enterprise License',
contact_id: contact.id,
company_id: company.id,
value: 50000,
stage: 'qualified',
probability: 60,
expected_close_date: '2025-03-31'
});
Authentication
Required Headers
All API requests require authentication:
Authorization: Bearer [JWT_TOKEN]
X-API-Key: [COMPANY_API_KEY]
Content-Type: application/json
API Reference
Contacts
Create Contact
Create a new contact record.
POST /api/v1/contacts
Request Body:
{
"data": {
"type": "contacts",
"attributes": {
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@company.com",
"phone": "+1-555-0123",
"title": "VP of Engineering",
"company_id": "comp_abc123",
"tags": ["enterprise", "decision-maker"],
"custom_fields": {
"department": "Engineering",
"budget_authority": true
}
}
}
}
Response:
{
"data": {
"id": "cont_xyz789",
"type": "contacts",
"attributes": {
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@company.com",
"phone": "+1-555-0123",
"title": "VP of Engineering",
"company_id": "comp_abc123",
"tags": ["enterprise", "decision-maker"],
"custom_fields": {
"department": "Engineering",
"budget_authority": true
},
"lead_score": 0,
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}
}
}
Get Contact
Retrieve a contact by ID.
GET /api/v1/contacts/:id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Related resources: company,deals,activities |
List Contacts
Retrieve a paginated list of contacts.
GET /api/v1/contacts
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
page[number] | integer | Page number (default: 1) |
page[size] | integer | Items per page (default: 25, max: 100) |
filter[email] | string | Filter by email (exact match) |
filter[company_id] | string | Filter by company |
filter[tags] | string | Filter by tags (comma-separated) |
filter[lead_score_min] | integer | Minimum lead score |
filter[lead_score_max] | integer | Maximum lead score |
sort | string | Sort field: created_at, -created_at, lead_score, -lead_score |
Update Contact
Update an existing contact.
PATCH /api/v1/contacts/:id
Delete Contact
Delete a contact (soft delete by default).
DELETE /api/v1/contacts/:id
Merge Contacts
Merge duplicate contacts into one.
POST /api/v1/contacts/:id/merge
Companies
Create Company
Create a new company account.
POST /api/v1/companies
Request Body:
{
"data": {
"type": "companies",
"attributes": {
"name": "Acme Corp",
"domain": "acme.com",
"industry": "Technology",
"size": "100-500",
"website": "https://acme.com",
"phone": "+1-555-0100",
"address": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"country": "US"
},
"parent_company_id": null
}
}
}
List Companies
GET /api/v1/companies
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[industry] | string | Filter by industry |
filter[size] | string | Filter by company size |
filter[parent_company_id] | string | Filter by parent company |
sort | string | Sort: name, created_at, -account_score |
Get Company Hierarchy
Get the full company hierarchy (parent and children).
GET /api/v1/companies/:id/hierarchy
Deals
Create Deal
Create a new deal/opportunity.
POST /api/v1/deals
Request Body:
{
"data": {
"type": "deals",
"attributes": {
"name": "Enterprise License - Acme Corp",
"contact_id": "cont_xyz789",
"company_id": "comp_abc123",
"pipeline_id": "pipe_default",
"stage": "qualified",
"value": 50000,
"currency": "USD",
"probability": 60,
"expected_close_date": "2025-03-31"
}
}
}
Move Deal Stage
Move a deal to a different pipeline stage.
POST /api/v1/deals/:id/stage
Request Body:
{
"stage": "proposal",
"probability": 75,
"notes": "Sent proposal, awaiting response"
}
Win Deal
Mark a deal as won.
POST /api/v1/deals/:id/win
Lose Deal
Mark a deal as lost.
POST /api/v1/deals/:id/lose
List Deals by Pipeline
Get deals grouped by pipeline stage.
GET /api/v1/pipelines/:pipeline_id/deals
Pipelines
Create Pipeline
Create a custom sales pipeline.
POST /api/v1/pipelines
Request Body:
{
"data": {
"type": "pipelines",
"attributes": {
"name": "Enterprise Sales",
"stages": [
{ "name": "lead", "order": 1, "probability": 10 },
{ "name": "qualified", "order": 2, "probability": 25 },
{ "name": "proposal", "order": 4, "probability": 60 },
{ "name": "won", "order": 6, "probability": 100, "is_closed": true, "is_won": true },
{ "name": "lost", "order": 7, "probability": 0, "is_closed": true, "is_won": false }
]
}
}
}
Activities
Create Activity
Log a sales activity.
POST /api/v1/activities
Request Body:
{
"data": {
"type": "activities",
"attributes": {
"type": "call",
"subject": "Discovery Call",
"contact_id": "cont_xyz789",
"deal_id": "deal_def456",
"notes": "Discussed requirements. Interested in enterprise features.",
"duration_minutes": 30,
"outcome": "positive",
"completed_at": "2025-01-15T14:00:00Z"
}
}
}
Activity Types:
| Type | Description |
|---|---|
call | Phone call |
email | Email sent/received |
meeting | In-person or virtual meeting |
task | To-do task |
note | General note |
Get Contact Timeline
Get all activities for a contact as a timeline.
GET /api/v1/contacts/:id/timeline
Lead Scoring
Get Lead Score
Get the current lead score and breakdown.
GET /api/v1/contacts/:id/score
Response:
{
"data": {
"contact_id": "cont_xyz789",
"total_score": 85,
"breakdown": {
"behavioral": 45,
"demographic": 25,
"engagement": 15
},
"factors": [
{ "name": "Email opened (5x)", "points": 10 },
{ "name": "Website visited", "points": 15 },
{ "name": "Pricing page viewed", "points": 20 },
{ "name": "Enterprise company size", "points": 15 }
],
"grade": "A"
}
}
Configure Scoring Rules
Define custom lead scoring rules.
POST /api/v1/scoring-rules
Custom Fields
Create Custom Field
Define a custom field for contacts, companies, or deals.
POST /api/v1/custom-fields
Field Types:
| Type | Description |
|---|---|
text | Single-line text |
textarea | Multi-line text |
number | Numeric value |
currency | Money value |
date | Date value |
boolean | Yes/No |
dropdown | Single select |
multiselect | Multiple select |
Webhooks
Webhook Events
Subscribe to CRM events in real-time.
| Event | Description |
|---|---|
contact.created | New contact created |
contact.updated | Contact updated |
contact.deleted | Contact deleted |
company.created | New company created |
deal.created | New deal created |
deal.stage_changed | Deal moved stages |
deal.won | Deal marked as won |
deal.lost | Deal marked as lost |
activity.created | Activity logged |
lead_score.changed | Lead score updated |
Create Webhook
POST /api/v1/webhooks
SDK Examples
TypeScript
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { crm: 'https://crm.api.us.23blocks.com' },
apiKey: process.env.BLOCKS_API_KEY!,
});
// Create contact with company
async function createLead(data: LeadData) {
let company = await client.crm.companies.findByDomain(data.companyDomain);
if (!company) {
company = await client.crm.companies.create({
name: data.companyName,
domain: data.companyDomain
});
}
const contact = await client.crm.contacts.create({
first_name: data.firstName,
last_name: data.lastName,
email: data.email,
company_id: company.id,
tags: ['new-lead']
});
return contact;
}
// Move deal through pipeline
async function advanceDeal(dealId: string, newStage: string) {
const deal = await client.crm.deals.updateStage(dealId, {
stage: newStage
});
if (newStage === 'proposal') {
await client.crm.tasks.create({
subject: 'Follow up on proposal',
deal_id: dealId,
due_date: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000),
priority: 'high'
});
}
return deal;
}
Error Codes
| Code | Description |
|---|---|
CRM001 | Contact not found |
CRM002 | Company not found |
CRM003 | Deal not found |
CRM004 | Pipeline not found |
CRM005 | Invalid stage transition |
CRM006 | Duplicate contact (same email) |
CRM007 | Custom field validation failed |
Rate Limits
| Plan | Requests/minute | Contacts | Companies | Deals |
|---|---|---|---|---|
| Free | 60 | 1,000 | 100 | 100 |
| Starter | 300 | 10,000 | 1,000 | 1,000 |
| Pro | 1,000 | 100,000 | 10,000 | 10,000 |
| Enterprise | Custom | Unlimited | Unlimited | Unlimited |
Related Resources
- Marketing Page - Feature overview and pricing
- Auth Block - User authentication integration
- Onboarding Block - Customer journey integration
- Files Block - Document attachments
Use This Block with AI Agents
This block ships an official Claude Code plugin from the public 23blocks marketplace (19 plugins, 205 skills). Your agent gets full working knowledge of this API — endpoints, identity registration, and conventions.
# one-time: add the 23blocks marketplace
claude plugin marketplace add https://github.com/23blocks-OS/ai-agents
# install this block's plugin
claude plugin install crm-block
Configure BLOCKS_API_URL, BLOCKS_API_KEY, and BLOCKS_AUTH_TOKEN. Register identity first: each block is autonomous — register the user with this block's identity endpoint before private calls (only user_unique_id is required; email/phone are optional notification-routing fields).
Full reference: AI Agents & Claude Code Plugins · github.com/23blocks-OS/ai-agents