asciisd/zoho/zoho-crm-development

Claude Code and agent skill by asciisd/zoho

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Build and work with Zoho CRM integration features, including module CRUD operations, Eloquent model sync, OAuth authentication, webhooks, and search.

Install

npx vskill@latest install asciisd/zoho/zoho-crm-development

SKILL.md

Zoho CRM Development

When to use this skill

Use this skill when:

  • Creating, reading, updating, or deleting Zoho CRM records
  • Setting up Eloquent model sync with Zoho CRM modules
  • Configuring OAuth authentication for Zoho CRM
  • Handling Zoho CRM webhooks
  • Searching or querying Zoho CRM data
  • Working with related records across Zoho CRM modules
  • Performing bulk operations on Zoho CRM records

Package Structure

Asciisd\Zoho\
├── Facades\Zoho # Facade for ZohoClient
├── ZohoClient # Factory for module instances
├── Models\
│ ├── ZohoModel # Abstract base — all CRUD/search/upsert methods
│ ├── ZohoContact # Contacts module
│ ├── ZohoAccount # Accounts module
│ ├── ZohoLead # Leads module
│ ├── ZohoDeal # Deals module
│ ├── ZohoTask # Tasks module
│ ├── ZohoEvent # Events module
│ ├── ZohoCall # Calls module
│ ├── ZohoNote # Notes module
│ ├── ZohoProduct # Products module
│ ├── ZohoInvoice # Invoices module
│ ├── ZohoOAuthToken # Eloquent model for stored tokens
│ └── ZohoSync # Polymorphic sync tracking model
├── Auth\OAuthManager # OAuth flow and token lifecycle
├── Storage\TokenStorage # Token persistence (cache/database)
├── Traits\SyncsWithZoho # Auto-sync Eloquent models to Zoho
├── Jobs\SyncModelToZoho # Queued sync job
├── Events\ # Webhook events
├── Exceptions\ # Typed exceptions
└── Http\Controllers\ZohoWebhookController

Accessing Modules

Always use the Zoho facade to access CRM modules:

php
use Asciisd\Zoho\Facades\Zoho;
$contacts = Zoho::contacts();
$leads = Zoho::leads();
$deals = Zoho::deals();
$accounts = Zoho::accounts();
$tasks = Zoho::tasks();
$events = Zoho::events();
$calls = Zoho::calls();
$notes = Zoho::notes();
$products = Zoho::products();
$invoices = Zoho::invoices();

You can also use module classes directly:

php
use Asciisd\Zoho\Models\ZohoContact;
$contact = ZohoContact::find('record_id');

CRUD Operations

Every module model inherits these static methods from ZohoModel:

php
// Create a record
$result = Zoho::contacts()->create([
'First_Name' => 'Jane',
'Last_Name' => 'Doe',
'Email' => 'jane@example.com',
'Phone' => '+1234567890',
]);
// Find a record by ID
$contact = Zoho::contacts()->find('5344xxxxxxxxxxxx');
// Get all records (paginated)
$contacts = Zoho::contacts()->all();
$contacts = Zoho::contacts()->all(['per_page' => 50]);
// Update a record
$result = Zoho::contacts()->update('5344xxxxxxxxxxxx', [
'Phone' => '+0987654321',
]);
// Delete a record
$deleted = Zoho::contacts()->delete('5344xxxxxxxxxxxx');
// Clone a record (copies all non-system fields)
$clone = Zoho::contacts()->clone('5344xxxxxxxxxxxx');
// Get record count
$count = Zoho::contacts()->count();

Module-Specific Behavior

Calls — Call_Duration normalization

ZohoCall overrides create, update, upsert, and updateMultiple to normalize Call_Duration before sending. Zoho's Calls API expects "mm:ss" (minutes:seconds), but the package accepts:

  • Bare numbers as minutes — 30 or "30" → "30:00", 90 → "90:00", 125 → "125:00"
  • `"HH:mm:ss"` strings — converted to total minutes:seconds → "01:02:05" becomes "62:05"
  • `"mm:ss"` strings — normalized with zero-padding → "5:30" becomes "05:30"
  • `null`, empty string, or non-numeric input — passed through unchanged
php
// Logging a 30-minute call against a Lead
Zoho::calls()->create([
'Subject' => 'Discovery call',
'Call_Type' => 'Outbound',
'Call_Duration' => 30, // sent as "30:00"
'What_Id' => $leadId, // What_Id for Leads, Accounts, Deals
'$se_module' => 'Leads',
]);
// Logging a call against a Contact (Who_Id, no $se_module needed)
Zoho::calls()->create([
'Subject' => 'Follow-up call',
'Call_Type' => 'Outbound',
'Call_Duration' => 15, // sent as "15:00"
'Who_Id' => $contactId, // Who_Id is for Contacts only
]);

`Who_Id` vs `What_Id`: Use Who_Id only for Contacts (the person). For Leads, Accounts, Deals, and other modules, use What_Id with $se_module set to the module API name.

Other modules pass payloads through unchanged — only Calls has this transformation.

Search Operations

php
// Search with Zoho criteria syntax
$results = Zoho::contacts()->search('(Last_Name:equals:Doe)');
$results = Zoho::leads()->search('(Company:starts_with:Acme)');
// Convenience search methods
$results = Zoho::contacts()->searchByEmail('jane@example.com');
$results = Zoho::contacts()->searchByPhone('+1234567890');

Search criteria syntax follows Zoho CRM conventions: (Field_Name:operator:value). Supported operators include equals, starts_with, contains, greater_than, less_than, between, etc.

Upsert (Create or Update)

php
$result = Zoho::contacts()->upsert(
[
'Email' => 'jane@example.com',
'Last_Name' => 'Doe',
'Phone' => '+1234567890',
],
['Email'] // duplicate check fields
);

Bulk Operations

php
// Update multiple records
$results = Zoho::contacts()->updateMultiple([
['id' => '111', 'Phone' => '+1111111111'],
['id' => '222', 'Phone' => '+2222222222'],
]);
// Delete multiple records
$results = Zoho::contacts()->deleteMultiple(['111', '222', '333']);

Related Records

php
// Get deals related to a contact
$deals = Zoho::contacts()->getRelatedRecords('contact_id', 'Deals');
// Get notes related to an account
$notes = Zoho::accounts()->getRelatedRecords('account_id', 'Notes');

Lead Conversion

php
$result = Zoho::leads()->convert('lead_id', [
'overwrite' => true,
'notify_lead_owner' => true,
]);

Field Metadata

php
// Get all fields for a module
$fields = Zoho::contacts()->getFieldMetadata();
// Clear cached field names
Zoho::contacts()->clearFieldCache();
ZohoModel::clearAllFieldCache();

Eloquent Model Sync

The SyncsWithZoho trait auto-syncs Eloquent models to Zoho CRM via queued jobs on create, update, and delete.

Required implementation
php
use Asciisd\Zoho\Traits\SyncsWithZoho;
class Customer extends Model
{
use SyncsWithZoho;
protected $fillable = ['name', 'email', 'phone', 'company'];
// Required: specify which Zoho CRM module this model maps to
public function getZohoModule(): string
{
return 'Contacts';
}
// Optional: map model attributes to Zoho field API names
public function getZohoFieldMapping(): array
{
return [
'name' => 'Last_Name',
'email' => 'Email',
'phone' => 'Phone',
'company' => 'Company',
];
}
// Optional: conditionally sync (defaults to true)
protected function shouldSyncToZoho(): bool
{
return $this->is_active && !empty($this->email);
}
// Optional: exclude sensitive fields from sync
public function getExcludedZohoFields(): array
{
return ['password', 'remember_token', 'api_key'];
}
}
Custom module support

For custom Zoho modules (or modules whose API names don't follow the standard naming convention like Contacts -> ZohoContact), the SyncModelToZoho job resolves the ZohoModel class using a 3-step chain:

  1. Model method — override getZohoModelClass() on the Eloquent model to return a specific class
  2. Config map — add an entry in zoho.modules mapping the module API name to a class
  3. Naming convention — falls back to Zoho + singular module name
php
// Option 1: Override getZohoModelClass() on the Eloquent model
class Property extends Model
{
use SyncsWithZoho;
public function getZohoModule(): string { return 'Property_Listings'; }
public function getZohoModelClass(): ?string
{
return \App\Zoho\ZohoPropertyListing::class;
}
public function getZohoFieldMapping(): array
{
return ['address' => 'Listing_Address', 'price' => 'Asking_Price'];
}
}
php
// Option 2: Config map in config/zoho.php (no model changes needed)
'modules' => [
'Property_Listings' => \App\Zoho\ZohoPropertyListing::class,
],

The custom ZohoModel class extends ZohoModel and sets MODULE_API_NAME:

php
use Asciisd\Zoho\Models\ZohoModel;
class ZohoPropertyListing extends ZohoModel
{
protected const MODULE_API_NAME = 'Property_Listings';
}
Multi-model sync

Multiple Eloquent models can sync to the same Zoho module. The polymorphic ZohoSync model (zohoable_type + zohoable_id) keeps each model's sync records independent — different field mappings, separate Zoho record IDs, no collisions.

php
// Both User and DemoAccount sync to Leads — each gets its own Zoho record
class User extends Model
{
use SyncsWithZoho;
public function getZohoModule(): string { return 'Leads'; }
public function getZohoFieldMapping(): array
{
return ['name' => 'Last_Name', 'email' => 'Email'];
}
}
class DemoAccount extends Model
{
use SyncsWithZoho;
public function getZohoModule(): string { return 'Leads'; }
public function getZohoFieldMapping(): array
{
return ['company_name' => 'Company', 'contact_email' => 'Email'];
}
}

Key details:

  • Each model instance creates a separate record in Zoho (1 User + 1 DemoAccount = 2 Leads)
  • withoutZohoSync is per-class — User::withoutZohoSync(...) does not affect DemoAccount
  • To converge multiple models on one Zoho record, customize SyncModelToZoho to use upsert with a duplicate check field (e.g. Email)
Sync helpers
php
// Get the Zoho record ID linked to this model
$zohoId = $customer->getZohoRecordId();
// Access the ZohoSync relationship
$sync = $customer->zohoSync;
// Sync immediately (bypasses queue)
$customer->syncToZohoNow('create');
// Disable sync temporarily
Customer::withoutZohoSync(function () {
Customer::create([...]); // will NOT trigger Zoho sync
});
Sync tracking

The zoho_syncs table stores polymorphic relationships between Eloquent models and Zoho records. The ZohoSync model provides scopes:

php
use Asciisd\Zoho\Models\ZohoSync;
ZohoSync::forModule('Contacts')->get();
ZohoSync::withZohoRecordId('5344xxxx')->first();
ZohoSync::synced()->get();
ZohoSync::notSynced()->get();

Webhook Handling

Routes are registered automatically:

  • POST /zoho/webhook — receives webhooks
  • GET /zoho/webhook — verification endpoint
  • GET /zoho/callback — OAuth callback
Listening to events
php
// In EventServiceProvider or a listener
use Asciisd\Zoho\Events\ZohoWebhookReceived;
use Asciisd\Zoho\Events\ZohoRecordCreated;
use Asciisd\Zoho\Events\ZohoRecordUpdated;
use Asciisd\Zoho\Events\ZohoRecordDeleted;
// Generic webhook event
Event::listen(ZohoWebhookReceived::class, function ($event) {
$payload = $event->payload;
$module = $event->module;
$type = $event->event;
$data = $event->getData();
});
// Specific CRUD events
Event::listen(ZohoRecordCreated::class, function ($event) {
$record = $event->record;
$module = $event->module;
$recordId = $event->getRecordId();
});

Set ZOHO_WEBHOOK_SECRET to enable HMAC-SHA256 signature verification.

OAuth Authentication

Initial setup
bash
php artisan zoho:setup
Managing tokens
bash
php artisan zoho:auth status # Check auth status
php artisan zoho:auth url # Get authorization URL
php artisan zoho:auth refresh # Refresh access token
php artisan zoho:auth revoke # Revoke token
php artisan zoho:token:refresh # Refresh with optional --clear-cache
Programmatic access

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
38
Repository forks
38
7-day trend score
18

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/asciisd/zoho/zoho-crm-development/badge)](https://verified-skill.com/skills/asciisd/zoho/zoho-crm-development)

Something wrong with this skill or its page? Tell us.