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: Use the same URL for all environments. Your API key determines the environment:
pk_test_*/sk_test_*→ Routes to Stagingpk_live_*/sk_live_*→ Routes to Production
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