Skip to content
Calendar v1.0 · Leads v1.2

API Reference

Complete documentation for the Fusion Calling APIs. Manage calendar events and leads programmatically — with copy-paste examples and a built-in request builder.

Get Started
Base URLhttps://app.fusioncalling.com

Quick Start

Get started with Fusion Calling APIs in 5 minutes.

Beginner

New to APIs? Learn the basics with step-by-step tutorials.

  • Learn API fundamentals
  • Make your first API call
  • Understand authentication
Start Tutorial

Integrator

Connect your existing tools quickly with ready-to-use guides.

  • Platform-specific guides
  • Ready-to-use templates
  • Quick setup instructions
View Guides

Developer

Deep dive into API capabilities with the complete reference.

  • Complete endpoint reference
  • Interactive request builder
  • Best practices
View Reference

Authentication

All Fusion Calling APIs use API key authentication via the Authorization header using the Bearer token format.

Getting Your API Key

  1. Log in to your Fusion Calling account
  2. Navigate to SettingsAPI Keys
  3. Click "Generate New API Key"
  4. Copy the API key (it's only shown once when created)

Using Your API Key

Include the API key in the Authorization header of every request:

Authorization: Bearer your-api-key-here

Warning

Header format is strict: the header must be exactly two whitespace-separated tokens, and the first token must be the literal Bearer (case-sensitive). Any other format, an empty key, or using the x-api-key header returns 401 Unauthorized — the x-api-key header is not supported.

Error Response Shapes

Fusion Calling uses two distinct error shapes depending on where the failure happens — handle both in your client:

Auth failure (401)

{
  "error": "Unauthorized",
  "message": "Invalid or missing API key"
}

Validation / route error

{
  "success": false,
  "error": "Description of what went wrong"
}

Authentication Examples

curl -X GET "https://app.fusioncalling.com/api/calendar/external/v1/events" \
  -H "Authorization: Bearer bf207bcf-c0de-4067-bd28-45728bd305aa"
bash

Warning

Security: Never expose your API key in client-side code, public repositories, or unsecured environments. Always use environment variables or secure secret management.
Authentication Best Practices
  • ✅ Store API keys in environment variables
  • ✅ Rotate API keys regularly
  • ✅ Use different keys for development and production
  • ✅ Implement key rotation in your deployment process
  • ✅ Monitor API key usage for unusual activity
  • ❌ Never commit API keys to version control
  • ❌ Never share API keys via email or chat

Your First Request

Make your first API call to verify your authentication and understand the response format.

1 · Test your API key

List your calendar events to confirm everything works:

curl -X GET "https://app.fusioncalling.com/api/calendar/external/v1/events" \
  -H "Authorization: Bearer YOUR_API_KEY"

2 · Create your first event

Create a simple event with only the required fields:

1curl -X POST "https://app.fusioncalling.com/api/calendar/external/v1/events" \
2  -H "Authorization: Bearer YOUR_API_KEY" \
3  -H "Content-Type: application/json" \
4  -d '{
5    "title": "My First API Event",
6    "start_time": "2025-12-15T14:30:00",
7    "end_time": "2025-12-15T16:00:00"
8  }'

Success

That's it! The response includes the event ID and other details. Now jump to the Calendar or Leads API to explore every endpoint with an interactive request builder.

Calendar API

Manage calendar events programmatically with full CRUD operations, integrated video conferencing (Google Meet and Zoom), and attendees management.

v2.0
Base URL
https://app.fusioncalling.com/api/calendar/external/v1
Version
v2.0
Rate limit
12 requests / minute
Auth
Bearer API key

Endpoints at a glance

GET
/api/calendar/external/v1/events

List calendar events

Use cases: Sync events to a dashboard · Fetch upcoming events for a date range
Query Parameters
fromOptional
string

Filter events from this date (inclusive). Flexible datetime formats.

e.g. 2025-12-01T00:00:00
toOptional
string

Filter events until this date (inclusive). Flexible datetime formats.

e.g. 2025-12-31T23:59:59
Success Response
200
1{
2  "success": true,
3  "data": [
4    {
5      "id": "550e8400-e29b-41d4-a716-446655440000",
6      "title": "Team Meeting",
7      "description": "Weekly team sync",
8      "start_time": "2025-12-15T14:30:00.000Z",
9      "end_time": "2025-12-15T16:00:00.000Z",
10      "all_day": false,
11      "google_meet_enabled": true,
12      "meet_link": "https://meet.google.com/abc-defg-hij",
13      "zoom_enabled": false,
14      "zoom_meeting_id": null,
15      "zoom_join_url": null,
16      "attendees": [
17        {
18          "email": "john@example.com",
19          "display_name": "John Doe",
20          "response_status": "accepted"
21        }
22      ],
23      "caller_phone": null,
24      "caller_name": null,
25      "source": "local",
26      "created_at": "2025-12-10T10:00:00.000Z",
27      "updated_at": "2025-12-10T10:00:00.000Z"
28    }
29  ]
30}

Need every status code and common errors? .

GET
/api/calendar/external/v1/events/{id}

Get a single event

Path Parameters
idRequired
UUID

The unique identifier of the event.

e.g. 550e8400-e29b-41d4-a716-446655440000
Success Response
200
1{
2  "success": true,
3  "data": {
4    "id": "550e8400-e29b-41d4-a716-446655440000",
5    "title": "Team Meeting",
6    "description": "Weekly team sync",
7    "start_time": "2025-12-15T14:30:00.000Z",
8    "end_time": "2025-12-15T16:00:00.000Z",
9    "all_day": false,
10    "google_meet_enabled": true,
11    "meet_link": "https://meet.google.com/abc-defg-hij",
12    "zoom_enabled": false,
13    "zoom_meeting_id": null,
14    "zoom_join_url": null,
15    "attendees": [
16      {
17        "email": "john@example.com",
18        "display_name": "John Doe",
19        "response_status": "accepted"
20      }
21    ],
22    "caller_phone": null,
23    "caller_name": null,
24    "source": "local",
25    "created_at": "2025-12-10T10:00:00.000Z",
26    "updated_at": "2025-12-10T10:00:00.000Z"
27  }
28}

Need every status code and common errors? .

POST
/api/calendar/external/v1/events

Create an event

Use cases: Book a meeting from a web form · Create events triggered by automations · Generate a Zoom or Google Meet link for a scheduled call
Request Body
titleRequired
string

Event title (1-255 characters).

e.g. Meeting with Client
start_timeRequired
string

Event start time (flexible datetime format).

e.g. 2025-12-15T14:30:00
end_timeRequired
string

Event end time (must be after start_time).

e.g. 2025-12-15T16:00:00
descriptionOptional
string

Event description (max 500 characters).

e.g. Discuss project requirements
all_dayOptional
boolean

Whether the event is all-day. Must be a boolean, not a string. Video conferencing cannot be enabled for all-day events.

e.g. false
google_meet_enabledOptional
boolean

Enable Google Meet link generation (requires Google Calendar connected). Mutually exclusive with zoom_enabled.

e.g. false
zoom_enabledOptional
boolean

Enable Zoom meeting link generation (requires Zoom connected in Settings → Integrations). Mutually exclusive with google_meet_enabled.

e.g. true
attendeesOptional
array<object>

List of attendees (max 50). Each has email, optional display_name, and optional response_status. Duplicates are removed automatically.

e.g. [{"email":"client@example.com","display_name":"Client Rep"}]
caller_phoneOptional
string

Caller's phone number in E.164 format (e.g. +15551234567). Create-only — triggers an appointment SMS and cannot be changed via PATCH.

e.g. +15551234567
caller_nameOptional
string

Caller's display name (max 200 characters). Create-only — used in the SMS template and cannot be changed via PATCH.

e.g. Jane Doe
Success Response
201
1{
2  "success": true,
3  "data": {
4    "id": "770e8400-e29b-41d4-a716-446655440002",
5    "title": "Meeting with Client",
6    "description": "Discuss project requirements",
7    "start_time": "2025-12-15T14:30:00.000Z",
8    "end_time": "2025-12-15T16:00:00.000Z",
9    "all_day": false,
10    "google_meet_enabled": false,
11    "meet_link": null,
12    "zoom_enabled": true,
13    "zoom_meeting_id": 123456789,
14    "zoom_join_url": "https://zoom.us/j/123456789",
15    "attendees": [
16      {
17        "email": "client@example.com",
18        "display_name": "Client Rep",
19        "response_status": "needsAction"
20      }
21    ],
22    "caller_phone": "+15551234567",
23    "caller_name": "Jane Doe",
24    "source": "local",
25    "created_at": "2025-12-11T10:00:00.000Z",
26    "updated_at": "2025-12-11T10:00:00.000Z"
27  }
28}

Need every status code and common errors? .

PATCH
/api/calendar/external/v1/events/{id}

Update an event

Path Parameters
idRequired
UUID

The unique identifier of the event.

e.g. 550e8400-e29b-41d4-a716-446655440000
Request Body
titleOptional
string

Event title (1-255 characters).

e.g. Updated Meeting Title
descriptionOptional
string

Event description. Use null to clear.

e.g. Updated description
start_timeOptional
string

Event start time (flexible datetime format).

e.g. 2025-12-15T15:00:00
end_timeOptional
string

Event end time (flexible datetime format).

e.g. 2025-12-15T17:00:00
all_dayOptional
boolean

Whether the event is all-day. Must be a boolean. Video conferencing cannot be enabled for all-day events.

e.g. false
google_meet_enabledOptional
boolean

Enable/disable Google Meet. Disabling deletes the existing Meet. Mutually exclusive with zoom_enabled.

e.g. false
zoom_enabledOptional
boolean

Enable/disable Zoom. Disabling deletes the existing Zoom meeting. Mutually exclusive with google_meet_enabled.

e.g. true
attendeesOptional
array<object>

Merged with the stored attendees list (max 50 total). Duplicates are removed by lowercase email; stored display_name/response_status are preserved unless overridden.

e.g. [{"email":"client@example.com","display_name":"Client Rep"}]
Success Response
200
1{
2  "success": true,
3  "data": {
4    "id": "550e8400-e29b-41d4-a716-446655440000",
5    "title": "Updated Meeting Title",
6    "description": "Updated description",
7    "start_time": "2025-12-15T15:00:00.000Z",
8    "end_time": "2025-12-15T17:00:00.000Z",
9    "all_day": false,
10    "google_meet_enabled": false,
11    "meet_link": null,
12    "zoom_enabled": true,
13    "zoom_meeting_id": 987654321,
14    "zoom_join_url": "https://zoom.us/j/987654321",
15    "attendees": [
16      {
17        "email": "client@example.com",
18        "display_name": "Client Rep",
19        "response_status": "needsAction"
20      }
21    ],
22    "caller_phone": "+15551234567",
23    "caller_name": "Jane Doe",
24    "source": "local",
25    "created_at": "2025-12-10T10:00:00.000Z",
26    "updated_at": "2025-12-11T11:30:00.000Z"
27  }
28}

Need every status code and common errors? .

DELETE
/api/calendar/external/v1/events/{id}

Delete an event

Path Parameters
idRequired
UUID

The unique identifier of the event.

e.g. 550e8400-e29b-41d4-a716-446655440000
Success Response
200
{
  "success": true,
  "message": "Event deleted successfully"
}

Need every status code and common errors? .

Datetime Formats

The API accepts many common formats and normalizes them to ISO 8601 (UTC) for storage.

FormatNotes
"2025-12-15T14:30:00"Simple — no timezone (assumes UTC). Recommended.
"2025-12-15T14:30:00Z"ISO 8601 without milliseconds, UTC.
"2025-12-15T14:30:00.000Z"Full ISO 8601 (RFC 3339) with milliseconds.
"2025-12-15T14:30:00+05:30"With timezone offset.
"2025-12-15 14:30:00"Space separator (assumes UTC).

Video Conferencing

Generate meeting links automatically by enabling google_meet_enabled or zoom_enabled on create/update. Google Meet returns a meet_link; Zoom returns a zoom_meeting_id and zoom_join_url.

Warning

Only one video conference provider can be enabled per event. Enabling both returns a 400 error. Video conferencing also cannot be enabled for all-day events.

Enable Google Meet

{
  "google_meet_enabled": true
}

Requires Google Calendar connected. Returns meet_link.

Enable Zoom

{
  "zoom_enabled": true
}

Requires Zoom connected (Settings → Integrations). Returns zoom_join_url.

Switch providers by disabling one and enabling the other in the same PATCH:

// Google Meet -> Zoom
{
  "google_meet_enabled": false,
  "zoom_enabled": true
}

Information

If the provider isn't connected, the event is still created/updated but the response includes a warnings array (e.g. ["zoom_not_connected"]) and the corresponding link fields stay null.

Boolean Field Rules

Fields like all_day, google_meet_enabled, and zoom_enabled must be real booleans, not strings.

✓ Correct

{
  "all_day": true,
  "google_meet_enabled": false,
  "zoom_enabled": true
}

✗ Incorrect

{
  "all_day": "true",
  "zoom_enabled": "false"
}

Warning

Sending booleans as strings (true, false, 1, 0) returns a validation error: "Expected boolean, received string".

Attendees

Pass an attendees array (max 50) on create or update. On update (PATCH) the new attendees are merged with the stored list — duplicate emails (matched case-insensitively) are removed, and stored display_name / response_status are preserved unless you override them.

1{
2  "attendees": [
3    {
4      "email": "client@example.com",
5      "display_name": "Client Rep",
6      "response_status": "needsAction"
7    },
8    {
9      "email": "team@example.com"
10    }
11  ]
12}

response_status accepts one of:

needsActionaccepteddeclinedtentative

Event Object

The full shape of a calendar event.

1{
2  "id": "uuid",                    // auto-generated
3  "title": "string",               // 1-255 chars (required)
4  "description": "string",         // optional, max 500
5  "start_time": "ISO 8601",        // required
6  "end_time": "ISO 8601",          // required, after start_time
7  "all_day": false,                // default false
8  "google_meet_enabled": false,    // default false
9  "meet_link": "string|null",      // response-only (Google Meet)
10  "zoom_enabled": false,           // default false
11  "zoom_meeting_id": 123456789,    // response-only (Zoom)
12  "zoom_join_url": "string|null",  // response-only (Zoom)
13  "attendees": [{                  // optional, max 50
14    "email": "…",
15    "display_name": "string",
16    "response_status": "needsAction|accepted|declined|tentative"
17  }],
18  "caller_phone": "string|null",   // optional, E.164 format
19  "caller_name": "string|null",    // optional, max 200 chars
20  "source": "local|google",        // response-only
21  "created_at": "ISO 8601",        // response-only
22  "updated_at": "ISO 8601"         // response-only
23}

Warning

caller_phone and caller_name are create-only (POST) — they cannot be changed via PATCH. When caller_phone is set on create, it triggers an appointment SMS. Internal delivery fields (sms_sent, sms_sent_at) exist on the record but are never returned by the external API.

Leads API

Send leads from your favorite tools to Fusion Calling. v1.2 adds read (GET), PATCH updates, Default Feed routing, and atomic batch inserts.

v1.2
Base URL
https://app.fusioncalling.com/api/leads/external/v1
Version
v1.2
Rate limit
100 requests / minute
Auth
Bearer API key

Fields & Default Feed

All request bodies use JSON with these fields.

Required

nameRequired
string

Lead's full name (2-100 characters).

e.g. John Doe
phoneRequired
string

10-25 characters raw, normalized to 10-15 digit E.164. +, spaces, -, () allowed.

e.g. 5551234567 or +1 234 567 8900

Optional

emailOptional
string

Valid email address (max 254 characters). Nullable.

e.g. john@example.com
companyOptional
string

Company name (max 100 characters). Nullable.

e.g. Acme Corp
tagsOptional
array<string>

Up to 20 tags. Each matches /^[a-zA-Z0-9_-]+$/ (1-50 chars). Nullable.

e.g. ["vip","website-lead"]
custom_fieldsOptional
object

Up to 20 keys (/^[a-zA-Z0-9_-]+$/). Values: string (max 1000), number, boolean, or null.

e.g. {"source":"website","score":8}
campaign_idOptional
UUID

Target campaign UUID. Omit to use the Default Feed (call-only).

e.g. omit → Default Feed
kindOptional
enum

Lead kind; must match the target campaign. 'sms' requires a campaign_id.

callsms

Information

Phone normalization: US 10-digit numbers work without a country code. +1 (234) 567-8900, 5551234567, and +15551234567 all match the same lead.

Warning

Breaking change (v1.2): If your integration previously omitted campaign_id, leads now route to the Default Feed instead of your oldest campaign.

Information

Status is system-managed: every externally-created lead is created with status: "pending" and call_attempts: 0. The status field is not settable through the external API — it progresses as the platform processes the lead.

Warning

SMS leads need a campaign: kind: "sms" requires a campaign_id. The Default Feed is call-only, so sending an SMS lead without a campaign returns a validation error.

Information

Private fields are never accepted or returned: the API strips and never exposes account_id, user_id, phone_normalized, failure_reason, cost, and sms_marketing_opt_out.

Endpoints at a glance

POST
/api/leads/external/v1/leads

Create a lead

Use cases: Capture a form submission · Send a CRM contact to Fusion Calling
Request Body
nameRequired
string

Lead's full name (2-100 characters).

e.g. John Doe
phoneRequired
string

Phone number, 10-25 characters raw, auto-normalized to 10-15 digit E.164.

e.g. +1 234 567 8900
emailOptional
string

Email address (max 254 characters). Send null to clear.

e.g. john@example.com
companyOptional
string

Company name (up to 100 characters). Send null to clear.

e.g. Acme Corp
tagsOptional
array<string>

Lead tags (max 20). Each tag must match /^[a-zA-Z0-9_-]+$/ (1-50 chars).

e.g. ["vip","website-lead"]
custom_fieldsOptional
object

Custom metadata (max 20 keys). Keys match /^[a-zA-Z0-9_-]+$/ (1-50 chars); values can be string (max 1000), number, boolean, or null.

e.g. {"source":"website","score":8}
campaign_idOptional
UUID

Target campaign UUID. Omit to route to the Default Feed (call-only).

kindOptional
enum

Lead kind. Must match the target campaign. 'sms' requires a campaign_id (the Default Feed is call-only).

callsms
e.g. call
Success Response
201
1{
2  "success": true,
3  "data": {
4    "mode": "atomic",
5    "inserted": 1,
6    "leads": [
7      {
8        "id": "f1c2d3e4-5b6a-7c8d-9e0f-1a2b3c4d5e6f",
9        "name": "John Doe",
10        "phone": "+12345678900",
11        "email": "john@example.com",
12        "company": "Acme Corp",
13        "campaign_id": "a1b2c3d4-...",
14        "campaign_name": "Default Feed",
15        "status": "pending",
16        "tags": ["vip", "website-lead"],
17        "custom_fields": { "source": "website", "score": 8 },
18        "call_attempts": 0,
19        "created_at": "2026-06-17T12:00:00.000Z"
20      }
21    ]
22  }
23}

Need every status code and common errors? .

POST
/api/leads/external/v1/leads

Create leads in batch

Request Body
leadsRequired
array<object>

Array of 1-100 lead objects, each with name & phone.

e.g. [{"name":"Lead 1","phone":"+1234567890"},{"name":"Lead 2","phone":"+1234567891"}]
kindOptional
enum

Batch-level kind. Must match the target campaign kind. 'sms' requires a campaign_id (the Default Feed is call-only).

callsms
e.g. call
Success Response
201
1{
2  "success": true,
3  "data": {
4    "mode": "atomic",
5    "inserted": 2,
6    "leads": [
7      { "id": "uuid-1", "name": "Lead 1", "phone": "+1234567890", "status": "pending" },
8      { "id": "uuid-2", "name": "Lead 2", "phone": "+1234567891", "status": "pending" }
9    ]
10  }
11}

Need every status code and common errors? .

GET
/api/leads/external/v1/leads

Lookup lead by phone

Query Parameters
phoneRequired
string

Phone number to lookup (US 10-digit works without country code).

e.g. +1234567890
campaign_idOptional
UUID

Optional UUID to scope the lookup to a specific campaign.

statusOptional
enum

Optional filter applied after lookup. A matched lead that does not satisfy it returns 404.

pendingin-progressretrycompletedfailed
updated_sinceOptional
string

Optional filter applied after lookup (any parseable datetime -> ISO UTC).

Success Response
200
1{
2  "success": true,
3  "data": {
4    "lookup": "phone",
5    "campaign_id": "a1b2c3d4-...",
6    "campaign_name": "Default Feed",
7    "lead": {
8      "id": "f1c2d3e4-5b6a-7c8d-9e0f-1a2b3c4d5e6f",
9      "name": "John Doe",
10      "phone": "+12345678900",
11      "email": "john@example.com",
12      "company": "Acme Corp",
13      "status": "pending",
14      "tags": ["vip"],
15      "custom_fields": { "source": "website" },
16      "call_attempts": 0,
17      "created_at": "2026-06-16T12:00:00.000Z",
18      "updated_at": "2026-06-16T12:00:00.000Z"
19    }
20  }
21}

Need every status code and common errors? .

GET
/api/leads/external/v1/leads

List leads (paginated)

Query Parameters
campaign_idOptional
UUID

Optional UUID. Omit to read from the Default Feed.

statusOptional
enum

Optional status filter. Omit to return leads of any status.

pendingin-progressretrycompletedfailed
e.g. pending
updated_sinceOptional
string

Only return leads updated since this datetime (any parseable format -> ISO UTC).

e.g. 2026-07-01T00:00:00
pageOptional
number

Page number (default 1).

e.g. 1
pageSizeOptional
number

Results per page (default 50, min 1, max 100).

e.g. 50
sortOptional
enum

Sort order (default created_at:desc).

created_at:desccreated_at:ascupdated_at:desc
e.g. created_at:desc
Success Response
200
1{
2  "success": true,
3  "data": {
4    "campaign_id": "a1b2c3d4-...",
5    "campaign_name": "Default Feed",
6    "page": 1,
7    "pageSize": 50,
8    "total": 2,
9    "leads": [
10      { "id": "uuid-1", "name": "John Doe", "phone": "+12345678900", "status": "pending", "tags": ["vip"], "created_at": "2026-06-16T12:00:00.000Z" },
11      { "id": "uuid-2", "name": "Jane Roe", "phone": "+12345678901", "status": "pending", "tags": null, "created_at": "2026-06-16T12:05:00.000Z" }
12    ]
13  }
14}

Need every status code and common errors? .

GET
/api/leads/external/v1/leads/{lead_id}

Get a lead by ID

Path Parameters
lead_idRequired
UUID

Lead UUID from the POST response.

e.g. f1c2d3e4-5b6a-7c8d-9e0f-1a2b3c4d5e6f
Success Response
200
1{
2  "success": true,
3  "data": {
4    "lead": {
5      "id": "f1c2d3e4-5b6a-7c8d-9e0f-1a2b3c4d5e6f",
6      "name": "John Doe",
7      "phone": "+12345678900",
8      "email": "john@example.com",
9      "company": "Acme Corp",
10      "campaign_id": "a1b2c3d4-...",
11      "campaign_name": "Default Feed",
12      "status": "pending",
13      "tags": ["vip"],
14      "custom_fields": { "source": "website" },
15      "call_attempts": 0,
16      "created_at": "2026-06-16T12:00:00.000Z",
17      "updated_at": "2026-06-16T12:00:00.000Z"
18    }
19  }
20}

Need every status code and common errors? .

PATCH
/api/leads/external/v1/leads

Update a lead

Request Body
phoneRequired
string

Lookup key that identifies the lead (normalized to E.164). The phone number itself cannot be changed.

e.g. +1234567890
campaign_idOptional
UUID

Optional scoping campaign UUID. Omit to target the Default Feed.

kindOptional
enum

Updated lead kind. 'sms' requires a campaign_id (the Default Feed is call-only).

callsms
nameOptional
string

Updated lead name (2-100 characters).

e.g. John Doe Updated
emailOptional
string

Updated email address (max 254). Send null to clear.

e.g. john.new@example.com
companyOptional
string

Updated company name (max 100). Send null to clear.

e.g. New Corp
tagsOptional
array<string>

Updated tags (max 20). Send null to clear.

e.g. vip,updated
custom_fieldsOptional
object

Merged into the existing custom_fields (keys max 20). Set a key to null to remove it.

e.g. {"score":9}
Success Response
200
1{
2  "success": true,
3  "data": {
4    "lead": {
5      "id": "f1c2d3e4-5b6a-7c8d-9e0f-1a2b3c4d5e6f",
6      "name": "John Doe Updated",
7      "phone": "+12345678900",
8      "email": "john.new@example.com",
9      "company": "New Corp",
10      "campaign_name": "Default Feed",
11      "status": "pending",
12      "tags": ["vip", "updated"],
13      "custom_fields": { "source": "website", "score": 9 },
14      "call_attempts": 0,
15      "created_at": "2026-06-17T12:00:00.000Z",
16      "updated_at": "2026-06-17T12:05:00.000Z"
17    }
18  }
19}

Need every status code and common errors? .

Lead Object

The shape returned for a single lead. GET-by-ID and PATCH wrap it under data.lead; phone lookup wraps it under data.lead with a lookup: "phone" marker.

1{
2  "id": "uuid",                       // always present
3  "name": "string",                   // always present
4  "phone": "string",                  // normalized E.164
5  "email": "string|null",
6  "company": "string|null",
7  "status": "pending",                // always "pending" on external create
8  "campaign_id": "uuid",
9  "campaign_name": "string",
10  "tags": ["string"]|null,            // present if set
11  "custom_fields": { ... }|null,      // present if set
12  "call_attempts": 0,                 // present if set
13  "last_called_at": "ISO 8601|null",  // present if set
14  "created_at": "ISO 8601",
15  "updated_at": "ISO 8601"            // present if set
16}

Note: account_id, user_id, phone_normalized, failure_reason, cost, and sms_marketing_opt_out are never returned.

FAQ

Integration Guides

Connect Fusion Calling with your favorite tools.

GoHighLevel

Send leads from GoHighLevel to Fusion Calling whenever a contact is created.

  1. Go to Settings → Automation → Webhooks
  2. Click "Add Webhook"
  3. URL: https://app.fusioncalling.com/api/leads/external/v1/leads
  4. Method: POST
  5. Header: Authorization: Bearer YOUR_API_KEY
1{
2  "name": "{{contact.name}}",
3  "phone": "{{contact.phone}}",
4  "email": "{{contact.email}}",
5  "tags": ["gohighlevel"]
6}

n8n

Connect any system with visual workflows using an HTTP Request node.

  • Method: POST
  • URL: https://app.fusioncalling.com/api/leads/external/v1/leads
  • Auth: Generic Credential · header Authorization = Bearer YOUR_API_KEY
  • Body content type: JSON

Information

Use JSON mode so boolean/array fields aren't sent as strings.

Zapier

Connect Fusion Calling with 5,000+ apps.

  • • Facebook Lead Ads → Fusion Calling
  • • Google Sheets → Fusion Calling
  • • Typeform → Fusion Calling
  • • HubSpot → Fusion Calling

Action: Webhooks by Zapier · POST to the Leads endpoint with the Authorization header.

Web Forms

Capture leads from website forms with server-side processing.

Warning

Never expose your API key in client-side JavaScript. Always process on the server.
1<?php
2$apiKey = 'YOUR_API_KEY';
3$endpoint = 'https://app.fusioncalling.com/api/leads/external/v1/leads';
4
5$data = [
6    'name'  => $_POST['name'],
7    'phone' => $_POST['phone'],
8    'email' => $_POST['email'],
9    'tags'  => ['website-form'],
10];
11
12$ch = curl_init($endpoint);
13curl_setopt($ch, CURLOPT_POST, true);
14curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
15curl_setopt($ch, CURLOPT_HTTPHEADER, [
16    'Authorization: Bearer ' . $apiKey,
17    'Content-Type: application/json',
18]);
19curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
20
21$response = curl_exec($ch);
22curl_close($ch);

Reference

Error codes, rate limiting, and best practices.

Error Codes

400Bad RequestCheck the request format and required fields.
401UnauthorizedVerify your API key in the Authorization header.
404Not FoundVerify the resource (and ID) exists.
409ConflictResource already exists — use PATCH to update.
429Too Many RequestsImplement retry with exponential backoff.
500Server ErrorRetry with backoff; contact support if it persists.

Rate Limiting

All Fusion Calling APIs implement rate limiting to protect the service from abuse and ensure fair usage across all users.

Calendar API

Limit

12 requests per minute

Window

Sliding window (last 60 seconds)

Scope

Per API key

Leads API

Limit

100 requests per minute

Window

Sliding window (last 60 seconds)

Scope

Per API key (each batch counts as 1 request)

Default (any other route)

Limit

12 requests per minute

Window

Sliding window (last 60 seconds)

Scope

Per API key

Rate Limit Headers

All API responses include rate limit headers to help you track your usage:

HeaderDescriptionExample
X-RateLimit-LimitMaximum requests allowed12
X-RateLimit-RemainingRequests remaining in current window8
X-RateLimit-ResetUnix timestamp when limit resets1704067200
Retry-AfterSeconds to wait before retry (429 only)30

429 Response Body

When the limit is exceeded, the JSON body echoes the same values as the headers so you can read either:

1{
2  "error": "Rate limit exceeded",
3  "message": "Too many requests...",
4  "retryAfter": 47,
5  "limit": 100,
6  "remaining": 0
7}

Handling Rate Limits

When you exceed the rate limit, you'll receive a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait before retrying.

Best Practices
  • ✅ Implement exponential backoff for retries
  • ✅ Cache responses to reduce unnecessary requests
  • ✅ Use batch operations when available
  • ✅ Monitor rate limit headers in production
  • ✅ Queue requests when approaching rate limits

Information

Tip: Each API key has independent rate limits. If you need higher throughput, consider using multiple API keys for different services or environments.
Example: Implementing Retry Logic
async function makeRequestWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);
    
    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : 5000;
      
      // Exponential backoff
      const backoffTime = waitTime * Math.pow(2, i);
      await new Promise(resolve => setTimeout(resolve, backoffTime));
      continue;
    }
    
    return response;
  }
  
  throw new Error('Max retries exceeded');
}

Best Practices

Security

  • ✓ Store API keys in environment variables
  • ✓ Use HTTPS for all requests
  • ✓ Rotate keys regularly
  • ✗ Never expose keys in client-side code
  • ✗ Never commit keys to version control

Performance

  • ✓ Use batch operations for many items
  • ✓ Cache responses where appropriate
  • ✓ Implement exponential backoff for 429s
  • ✓ Use pagination for large datasets

Changelog

External API Hardening

July 2026
  • custom_fields support on lead create/update (merged on PATCH)
  • updated_since + sort filters added to List Leads
  • Writable-fields allowlist — sensitive columns never accepted or returned
  • Attendees now merge (not replace) on event PATCH
  • caller_phone / caller_name are create-only; caller_phone triggers an appointment SMS
  • Stricter Authorization header parsing; documented 401 vs validation error shapes
  • 429 response body + default 12/min fallback documented

Leads API v1.2

February 2026
  • GET endpoints for reading leads
  • PATCH endpoint for updating leads
  • Default Feed campaign routing
  • Atomic batch inserts (1-100)

Calendar API v1.0

December 2025
  • Initial release with full CRUD
  • Flexible datetime formats
  • Google Meet integration
  • Automatic Google Calendar sync

Support

Information

For API support, email hello@fusioncalling.com.