CRM & Website Integration API Reference

The Valethon CRM API enables external websites, marketing platforms, and third-party tools to integrate with the CRM module. It provides lead capture, contact management, opportunity tracking, and embeddable chat widgets for MSP customer websites.

Base URL: https://{tenant}.valethon.com/admin/api/v1/crm


Authentication

The CRM API uses API keys for authentication. Keys are managed in Settings → CRM → API Keys.

API Key Types

PrefixTypeUse Case
pk_live_*Public Key (Live)Production chat widget embed
pk_test_*Public Key (Test)Development/staging widget
crm_*Secret KeyServer-side API access (leads, contacts, opportunities)

Authentication Header


X-API-Key: crm_a1b2c3d4e5f6...

Public keys (pk_live_, pk_test_) are used client-side in the chat widget embed and only grant access to widget endpoints.

Secret keys (crm_*) are used server-side and grant access to all CRM API endpoints. Never expose secret keys in client-side code.

Key Management

Keys are stored securely using password_hash() with only the prefix visible in the admin UI. Each key has:


Rate Limiting

The API enforces rate limits using a sliding window algorithm (60-second window).

Key TypeDefault Limit
Public keys (pk_*)30 requests/minute
Secret keys (crm_*)120 requests/minute

Rate limit headers are included in every response:


X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1710350400

When exceeded, a 429 Too Many Requests response is returned with a Retry-After header.


Response Format

All responses return JSON with consistent structure:

Success:


{
  "success": true,
  "data": { ... }
}

Error:


{
  "success": false,
  "error": "Description of the error",
  "code": "VALIDATION_ERROR"
}

Paginated:


{
  "success": true,
  "data": [ ... ],
  "pagination": {
    "page": 1,
    "per_page": 25,
    "total": 150,
    "total_pages": 6
  }
}

Error Codes

CodeHTTP StatusMeaning
AUTH_REQUIRED401Missing or invalid API key
FORBIDDEN403Key lacks required permission
NOT_FOUND404Resource not found
VALIDATION_ERROR400Invalid request data
RATE_LIMITED429Rate limit exceeded
DUPLICATE409Duplicate resource (e.g., email already exists)
SERVER_ERROR500Internal server error

Chat Widget

POST /widget/init

Initialize a chat widget session. Used by the embeddable widget on MSP customer websites.

Authentication: Public key (pk_live_ or pk_test_)

Request:


{
  "visitor_id": "v_abc123",
  "page_url": "https://customer-site.com/support",
  "referrer": "https://google.com"
}

Response (200):


{
  "success": true,
  "session_id": "ws_98765",
  "greeting": "Hello! How can we help you today?",
  "branding": {
    "company_name": "Acme IT Solutions",
    "primary_color": "#1FB6FF",
    "logo_url": "https://cdn.valethon.com/logos/acme.png"
  }
}

POST /widget/message

Send a message from the widget visitor.

Request:


{
  "session_id": "ws_98765",
  "message": "I need help with my email"
}

Response (200):


{
  "success": true,
  "response": {
    "message": "I can help with email issues. Could you describe the problem?",
    "suggested_actions": ["Create ticket", "Check service status"]
  }
}

POST /widget/end

End a widget chat session.

GET /widget/status

Get service status for the widget display.


Leads

GET /leads

List leads with filtering and pagination.

Query Parameters:

ParameterTypeDefaultDescription
pageint1Page number
per_pageint25Items per page (max 100)
statusstringallnew, contacted, qualified, unqualified, converted, all
sourcestringFilter by lead source
assigned_tointFilter by assigned employee
searchstringSearch by name, email, or company
created_afterstringISO 8601 date filter
created_beforestringISO 8601 date filter
sortstringcreated_atSort field
orderstringdescasc or desc

Response (200):


{
  "success": true,
  "data": [
    {
      "lead_id": 42,
      "first_name": "John",
      "last_name": "Doe",
      "email": "john@example.com",
      "phone": "+1-555-0123",
      "company_name": "Acme Corp",
      "status": "new",
      "source": "website_form",
      "score": 75,
      "assigned_to": null,
      "created_at": "2026-03-10T14:30:00Z",
      "updated_at": "2026-03-10T14:30:00Z"
    }
  ],
  "pagination": { "page": 1, "per_page": 25, "total": 150, "total_pages": 6 }
}

POST /leads

Create a new lead.

Request:


{
  "first_name": "Jane",
  "last_name": "Smith",
  "email": "jane@company.com",
  "phone": "+1-555-9876",
  "company_name": "Globex Corp",
  "source": "website_form",
  "form_type": "contact_us",
  "raw_payload": {
    "role": "IT Director",
    "technicianCount": "11-25",
    "industry": "MSP",
    "message": "Looking for a PSA solution"
  }
}

Response (201):


{
  "success": true,
  "data": {
    "lead_id": 43,
    "status": "new",
    "created_at": "2026-03-13T10:00:00Z"
  }
}

GET /leads/{id}

Get lead details including activity history and linked entities.

PUT /leads/{id}

Update lead fields (status, assignment, score, custom fields).

DELETE /leads/{id}

Delete a lead.

POST /leads/{id}/convert

Convert a lead to a contact, prospect account, and optionally an opportunity.

Request:


{
  "create_contact": true,
  "create_prospect": true,
  "create_opportunity": true,
  "opportunity_name": "Acme Corp - Managed IT Services",
  "opportunity_value": 50000
}

Response (200):


{
  "success": true,
  "data": {
    "contact_id": 201,
    "prospect_id": 55,
    "opportunity_id": 78,
    "lead_status": "converted"
  }
}

POST /leads/{id}/activities

Log an activity against a lead.

GET /leads/{id}/activities

Get activity history for a lead.


Opportunities

GET /opportunities

List opportunities with filtering.

Query Parameters:

ParameterTypeDescription
pageintPage number
per_pageintItems per page
stagestringFilter by pipeline stage
statusstringopen, won, lost, all
assigned_tointFilter by owner
prospect_idintFilter by prospect account
min_valuenumberMinimum deal value
max_valuenumberMaximum deal value

POST /opportunities

Create a new opportunity.

Request:


{
  "name": "Acme Corp - Managed Services",
  "prospect_id": 55,
  "stage": "discovery",
  "value": 50000,
  "expected_close_date": "2026-06-30",
  "probability": 30,
  "assigned_to": 5
}

GET /opportunities/{id}

Get opportunity details with activities, notes, and linked entities.

PUT /opportunities/{id}

Update opportunity fields.

DELETE /opportunities/{id}

Delete an opportunity.

POST /opportunities/{id}/advance-stage

Advance to the next pipeline stage.


People (Contacts)

GET /people

List CRM contacts.

Query Parameters:

ParameterTypeDescription
pageintPage number
per_pageintItems per page
searchstringSearch by name or email
prospect_idintFilter by linked prospect
tagstringFilter by tag

POST /people

Create a new contact.

GET /people/{id}

Get contact details.

PUT /people/{id}

Update contact fields.

DELETE /people/{id}

Delete a contact.


Prospect Accounts

GET /prospects

List prospect accounts.

Query Parameters:

ParameterTypeDescription
pageintPage number
per_pageintItems per page
searchstringSearch by account name
statusstringactive, inactive, converted
industrystringFilter by industry

POST /prospects

Create a new prospect account.

GET /prospects/{id}

Get prospect details with linked contacts, opportunities, and activities.

PUT /prospects/{id}

Update prospect fields.

DELETE /prospects/{id}

Delete a prospect account.

POST /prospects/{id}/convert

Convert a prospect to a full customer (creates a company record).


Activities

GET /activities

List activities across all CRM entities.

Query Parameters:

ParameterTypeDescription
pageintPage number
per_pageintItems per page
typestringcall, email, meeting, task, note
entity_typestringlead, contact, opportunity, prospect
entity_idintFilter by specific entity
assigned_tointFilter by assignee
completedbooleanFilter by completion status

POST /activities

Create an activity.

Request:


{
  "type": "call",
  "subject": "Discovery call with Acme Corp",
  "description": "Discuss current IT infrastructure and pain points",
  "entity_type": "opportunity",
  "entity_id": 78,
  "assigned_to": 5,
  "due_date": "2026-03-15T14:00:00Z",
  "duration_minutes": 30
}

GET /activities/{id}

Get activity details.

PUT /activities/{id}

Update activity (mark complete, reschedule, reassign).

DELETE /activities/{id}

Delete an activity.


Notes

POST /notes

Add a note to any CRM entity.

Request:


{
  "entity_type": "lead",
  "entity_id": 42,
  "content": "Spoke with John. Interested in managed services for 50 endpoints.",
  "is_pinned": false
}

GET /notes

List notes for an entity.

Query Parameters:

ParameterTypeDescription
entity_typestringlead, contact, opportunity, prospect
entity_idintEntity ID
pageintPage number

PUT /notes/{id}

Update a note.

DELETE /notes/{id}

Delete a note.


Events & Webhooks

The CRM API can send webhook notifications when events occur.

GET /webhooks

List configured webhooks.

POST /webhooks

Register a new webhook.

Request:


{
  "url": "https://your-app.com/webhooks/crm",
  "events": ["lead.created", "lead.converted", "opportunity.won"],
  "secret": "your_webhook_secret"
}

DELETE /webhooks/{id}

Remove a webhook registration.

Event Types

EventTrigger
lead.createdNew lead created
lead.updatedLead fields updated
lead.convertedLead converted to contact/prospect
lead.deletedLead deleted
opportunity.createdNew opportunity created
opportunity.stage_changedOpportunity moved to new stage
opportunity.wonOpportunity marked as won
opportunity.lostOpportunity marked as lost
contact.createdNew contact created
contact.updatedContact fields updated
prospect.createdNew prospect account
prospect.convertedProspect converted to customer
activity.completedActivity marked as complete

Webhook Payload:


{
  "event": "lead.created",
  "timestamp": "2026-03-13T10:00:00Z",
  "data": {
    "lead_id": 43,
    "email": "jane@company.com",
    "source": "website_form"
  },
  "signature": "sha256=a1b2c3..."
}

Verify webhook signatures using HMAC-SHA256 with your webhook secret.


Custom Fields

GET /custom-fields

List custom field definitions for a CRM entity type.

Query Parameters:

ParameterTypeDescription
entity_typestringlead, contact, opportunity, prospect

POST /custom-fields

Create a new custom field definition.

PUT /custom-fields/{id}

Update a custom field definition.

DELETE /custom-fields/{id}

Remove a custom field definition.


Search

GET /search

Search across all CRM entities.

Query Parameters:

ParameterTypeDescription
qstringSearch query
typesstringComma-separated entity types to search
limitintMax results (default 20)

Response (200):


{
  "success": true,
  "data": {
    "results": [
      {
        "type": "lead",
        "id": 42,
        "title": "John Doe",
        "subtitle": "Acme Corp",
        "score": 0.95,
        "url": "/crm/leads/42"
      }
    ],
    "total": 1
  }
}

Chat Widget Embed

To embed the CRM chat widget on an MSP customer website:


<script>
  window.ValethonWidget = {
    publicKey: 'pk_live_xxxxxxxxxxxx',
    position: 'bottom-right',
    primaryColor: '#1FB6FF',
    greeting: 'Need IT help? Chat with us!'
  };
</script>
<script src="https://{tenant}.valethon.com/widget/chat.js" async></script>

Configuration Options

OptionTypeDefaultDescription
publicKeystringRequiredYour public API key
positionstringbottom-rightWidget position: bottom-right, bottom-left
primaryColorstring#1FB6FFWidget accent color
greetingstringHi! How can we help?Initial greeting message
showOnMobilebooleantrueDisplay on mobile devices
autoOpenbooleanfalseAuto-open after delay
autoOpenDelaynumber5000Auto-open delay in ms

Permissions Reference

PermissionControls
leads:readView leads
leads:writeCreate and update leads
leads:deleteDelete leads
contacts:readView contacts
contacts:writeCreate and update contacts
opportunities:readView opportunities
opportunities:writeCreate and update opportunities
prospects:readView prospect accounts
prospects:writeCreate and update prospects
activities:readView activities
activities:writeCreate and update activities
webhooks:manageConfigure webhooks
widget:accessChat widget access (public keys)

For the Helpdesk API, see Helpdesk API Reference.

For the ServiceOS admin API, see ServiceOS API Reference.