How to Connect Any CRM via HTTP Request Node
Generic guide for Clio, Karbon, ServiceNow, Cosential, etc. REST API basics, auth headers, POST requests.
How to Connect Any CRM CRMClick to read the full definition in our AI & Automation Glossary. via HTTP Request Node
Most professional services CRMs
What You Need Before Starting
Pull up your CRM
Base URL: The root endpoint for all API
- Clio:
https://app.clio.com/api/v4 - Karbon:
https://api.karbonhq.com/v3 - ServiceNow:
https://[your-instance].service-now.com/api/now - Cosential:
https://[your-firm].cosential.com/api
Authentication method: Most use one of three approaches:
- APIkey in header (Clio, Karbon)APIClick to read the full definition in our AI & Automation Glossary.
- OAuth 2.0 with bearer token (ServiceNow, some Clio instances)
- Basic auth with username/password (older systems)
Rate limits: Note the requests-per-minute cap. Clio allows 500/min, Karbon allows 120/min. You'll need this for error handling.
Endpoint paths: Identify the exact paths for your use case. Creating a contact in Clio uses /contacts.json, while Karbon uses /contacts.
Setting Up Authentication
Authentication goes wrong more than anything else. Here's how to configure each type in n8n.
API APIClick to read the full definition in our AI & Automation Glossary. Key in Header
Most common for legal and accounting CRMs
- Set Authentication to "Generic Credential Type"
- Click "Create New Credential"
- Select "Header Auth"
- Name:
Authorization - Value:
Bearer [your-api-key]orToken [your-api-key](check your CRM's docs for exact format)CRMClick to read the full definition in our AI & Automation Glossary.
For Clio specifically:
- Name:
Authorization - Value:
Bearer YOUR_ACCESS_TOKEN
For Karbon:
- Name:
AccessKey - Value:
YOUR_API_KEY - Add second header:
Acceptwith valueapplication/json
OAuth 2.0
ServiceNow and enterprise CRMs
- Set Authentication to "OAuth2"
- Grant Type: "Authorization Code" (most common) or "Client Credentials"
- Authorization URL:
https://[your-instance].service-now.com/oauth_auth.do - Access Token URL:
https://[your-instance].service-now.com/oauth_token.do - Scope: Check docs (ServiceNow uses
useraccount)
Click "Connect my account" and complete the OAuth flow in the popup window.
Basic Auth
Older systems or internal APIs
- Set Authentication to "Basic Auth"
- Username: Your APIusernameAPIClick to read the full definition in our AI & Automation Glossary.
- Password: Your APIpassword or tokenAPIClick to read the full definition in our AI & Automation Glossary.
Creating Records (POST Requests)
Let's create a contact in Clio as a complete example. This pattern works for any CRM
HTTP Request Node Configuration:
Method: POST
URL: https://app.clio.com/api/v4/contacts.json
Authentication: Header Auth (configured above)
Headers to Add:
Content-Type:application/jsonAccept:application/json
Body (JSON format):
{
"data": {
"first_name": "`{{ $json.firstName }}`",
"last_name": "`{{ $json.lastName }}`",
"type": "Person",
"email_addresses": [
{
"name": "Work",
"address": "`{{ $json.email }}`",
"default_email": true
}
],
"phone_numbers": [
{
"name": "Mobile",
"number": "`{{ $json.phone }}`",
"default_number": true
}
]
}
}
For Karbon (different structure):
{
"PreferredName": "`{{ $json.firstName }}`",
"FamilyName": "`{{ $json.lastName }}`",
"EmailAddresses": [
{
"Email": "`{{ $json.email }}`",
"IsPrimary": true
}
]
}
Notice the differences: Clio wraps everything in a data object and uses snake_case. Karbon uses PascalCase with no wrapper. Always check your CRM
Reading Records (GET Requests)
Retrieving data requires query parameters for filtering and pagination.
Fetch all contacts modified in last 24 hours (Clio):
Method: GET
URL: https://app.clio.com/api/v4/contacts.json
Query Parameters:
updated_since:{{ $now.minus({hours: 24}).toISO() }}fields:id,first_name,last_name,email_addresses,updated_atlimit:200
Fetch specific contact by email (Karbon):
Method: GET
URL: https://api.karbonhq.com/v3/Contacts
Query Parameters:
$filter:EmailAddresses/any(e: e/Email eq '{{ $json.email }}')$select:ContactKey,PreferredName,FamilyName
Karbon uses OData query syntax. ServiceNow uses sysparm_query. Read your CRM
Updating Records (PUT/PATCH Requests)
Most CRMs
Update contact phone number (Clio):
Method: PUT
URL: https://app.clio.com/api/v4/contacts/{{ $json.contactId }}.json
Body:
{
"data": {
"phone_numbers": [
{
"id": "`{{ $json.phoneNumberId }}`",
"number": "`{{ $json.newPhone }}`"
}
]
}
}
You need the contact ID and the phone number ID. Always fetch the record first to get nested object IDs.
Handling Pagination
CRMs
Clio pagination pattern:
- Add Loop node after HTTP Request
- Set mode to "Run Once for Each Item"
- In HTTP Request, add query parameter:
cursor:{{ $json.meta.paging.next }}
- Loop continues until
meta.paging.nextis null
Karbon pagination pattern:
- Query parameter:
$skip:{{ $runIndex * 100 }} - Query parameter:
$top:100 - Loop until response returns fewer than 100 records
Error Handling You Actually Need
Add an IF node immediately after your HTTP Request node.
Condition 1 (Success):
{{ $json.statusCode }}equals200or201- Route to your success path
Condition 2 (Rate Limit):
{{ $json.statusCode }}equals429- Add Wait node: 60 seconds
- Add Loop node to retry the request
Condition 3 (Auth Failure):
{{ $json.statusCode }}equals401or403- Send alert via Slack/email
- Stop workflow
Condition 4 (Not Found):
{{ $json.statusCode }}equals404- Handle gracefully (maybe create the record instead of updating)
Everything Else:
- Log the full response:
{{ JSON.stringify($json) }} - Send to error tracking system
Real-World Example: Sync Form Submission to CRM CRMClick to read the full definition in our AI & Automation Glossary.
Complete workflow for capturing a website form and creating a CRM
Trigger: Webhook (receives form POST)
Node 1: HTTP Request (Check if contact exists)
- Method: GET
- URL:
https://app.clio.com/api/v4/contacts.json?query={{ $json.email }}
Node 2: IF (Contact exists?)
- Condition:
{{ $json.data.length > 0 }}
Node 3a: HTTP Request (Update existing - true branch)
- Method: PUT
- URL:
https://app.clio.com/api/v4/contacts/{{ $json.data[0].id }}.json - Body: Updated fields only
Node 3b: HTTP Request (Create new - false branch)
- Method: POST
- URL:
https://app.clio.com/api/v4/contacts.json - Body: Full contact object
Node 4: Set (Extract contact ID)
contactId:{{ $json.data.id }}contactUrl:https://app.clio.com/contacts/{{ $json.data.id }}
Node 5: Slack (Notify team)
- Message:
"New contact created: {{ $json.data.first_name }} {{ $json.data.last_name }}"
This pattern works for any CRM
Testing Your Integration
Before running in production:
- Test with a sandbox/test account (Clio and ServiceNow provide these)
- Use n8n's "Execute Node" to test individual requests
- Check the "Binary Data" tab in n8n to see raw responses
- Verify rate limits by running 10 requests in quick succession
- Test error scenarios by using invalid IDs or malformed JSON
Create a test contact with email test+[timestamp]@yourfirm.com so you can identify and delete test data easily.
Common Mistakes to Avoid
Hardcoding IDs: Always use expressions like {{ $json.id }} instead of copying IDs from your CRM
Ignoring nested objects: Phone numbers, addresses, and custom fields are usually arrays of objects. You can't just pass a string.
Skipping field validation: CRMs{{ parseInt($json.value) }}, not {{ $json.value }}.
Not handling duplicates: Most CRMs
Forgetting timezones: Use ISO 8601 format for dates: {{ $now.toISO() }}. Don't send MM/DD/YYYY strings.
You now have everything needed to connect any REST API

Reviewed by Revenue Institute
This guide is actively maintained and reviewed by the implementation experts at Revenue Institute. As the creators of The AI Workforce Playbook, we test and deploy these exact frameworks for professional services firms scaling without new headcount.
Revenue Institute
Need help turning this guide into reality? Revenue Institute builds and implements the AI workforce for professional services firms.