Skip to main content

CRM Block

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

ServiceURL
CRMhttps://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 Staging
  • pk_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:

ParameterTypeDescription
includestringRelated resources: company,deals,activities

List Contacts

Retrieve a paginated list of contacts.

GET /api/v1/contacts

Query Parameters:

ParameterTypeDescription
page[number]integerPage number (default: 1)
page[size]integerItems per page (default: 25, max: 100)
filter[email]stringFilter by email (exact match)
filter[company_id]stringFilter by company
filter[tags]stringFilter by tags (comma-separated)
filter[lead_score_min]integerMinimum lead score
filter[lead_score_max]integerMaximum lead score
sortstringSort 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:

ParameterTypeDescription
filter[industry]stringFilter by industry
filter[size]stringFilter by company size
filter[parent_company_id]stringFilter by parent company
sortstringSort: 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:

TypeDescription
callPhone call
emailEmail sent/received
meetingIn-person or virtual meeting
taskTo-do task
noteGeneral 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:

TypeDescription
textSingle-line text
textareaMulti-line text
numberNumeric value
currencyMoney value
dateDate value
booleanYes/No
dropdownSingle select
multiselectMultiple select

Webhooks

Webhook Events

Subscribe to CRM events in real-time.

EventDescription
contact.createdNew contact created
contact.updatedContact updated
contact.deletedContact deleted
company.createdNew company created
deal.createdNew deal created
deal.stage_changedDeal moved stages
deal.wonDeal marked as won
deal.lostDeal marked as lost
activity.createdActivity logged
lead_score.changedLead 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

CodeDescription
CRM001Contact not found
CRM002Company not found
CRM003Deal not found
CRM004Pipeline not found
CRM005Invalid stage transition
CRM006Duplicate contact (same email)
CRM007Custom field validation failed

Rate Limits

PlanRequests/minuteContactsCompaniesDeals
Free601,000100100
Starter30010,0001,0001,000
Pro1,000100,00010,00010,000
EnterpriseCustomUnlimitedUnlimitedUnlimited