Documentation
Back to siteGet Started
Developer Documentation

LinkyCal API Documentation

Everything you need to integrate forms, booking, and contact management into your product.

Installation

The fastest way to add LinkyCal to your site is with the embeddable widgets. Drop in a script tag and initialize with your project slug.

Booking Widgethtml
<!-- Booking Widget -->
<script src="https://cdn.linkycal.com/widgets/booking.js"></script>
<script>
  LinkyCal.booking({
    projectSlug: "your-project",
    container: "#booking-widget"
  });
</script>
Form Widgethtml
<!-- Form Widget -->
<script src="https://cdn.linkycal.com/widgets/form.js"></script>
<script>
  LinkyCal.form({
    projectSlug: "your-project",
    formSlug: "contact",
    container: "#form-widget"
  });
</script>

Quick Start

Get up and running in four steps:

  1. Create a project in the dashboard
  2. Create an event type or form
  3. Get your API key from MCP & APIs in the dashboard sidebar
  4. Start making API calls
Check available slotsbash
# Check available slots
curl -H "Authorization: Bearer lc_live_your_api_key" \
  "https://linkycal.com/api/v1/availability/your-project?date=2026-03-24&timezone=UTC&eventTypeSlug=consultation"

How It Works

Every interaction with LinkyCal follows a straightforward request/response flow:

  1. Client sends request to LinkyCal API
  2. Server validates input and checks availability/form config
  3. Action is performed (booking created, form step submitted, etc.)
  4. Response returned with result
  5. Optional: workflow triggers fire (email, webhook, tag)
All API responses follow a consistent JSON format. Errors include a descriptive error field.

Create Response

POST /api/v1/forms/:projectSlug/:formSlug/responses

Start a new form response. Returns the response object and full form config including all steps and fields.

PropertyTypeRequiredDescription
projectSlugstringYesYour project's URL slug (path param)
formSlugstringYesThe form's URL slug (path param)
Requestbash
curl -X POST "https://linkycal.com/api/v1/forms/acme/contact/responses" \
  -H "Content-Type: application/json"
Responsejson
{
  "response": {
    "id": "resp_a1b2c3d4",
    "formId": "form_x1y2z3",
    "currentStepIndex": 0,
    "status": "in_progress"
  },
  "form": {
    "id": "form_x1y2z3",
    "name": "Contact Form",
    "steps": [
      {
        "id": "step_1",
        "title": "Your Details",
        "fields": [
          { "id": "fld_1", "type": "text", "label": "Full Name", "required": true },
          { "id": "fld_2", "type": "email", "label": "Email", "required": true }
        ]
      }
    ]
  }
}

Submit Step

PATCH /api/v1/forms/:projectSlug/:formSlug/responses/:responseId/steps/:stepIndex

Submit field values for a specific step. Steps must be submitted in order (0, 1, 2...). When the last step is submitted, the response is automatically marked as completed.

PropertyTypeRequiredDescription
fieldsarrayYesArray of { fieldId, value } objects
Requestbash
curl -X PATCH "https://linkycal.com/api/v1/forms/acme/contact/responses/resp_a1b2c3d4/steps/0" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": [
      { "fieldId": "fld_1", "value": "Jane Smith" },
      { "fieldId": "fld_2", "value": "jane@example.com" }
    ]
  }'
Responsejson
{
  "response": {
    "id": "resp_a1b2c3d4",
    "status": "in_progress",
    "currentStepIndex": 1,
    "completedAt": null
  }
}
When the response status changes to completed, all steps have been submitted.

Native HTML Form

POST /api/public/forms/:projectSlug/:formSlug/submit

Post a regular browser form directly to LinkyCal without any client-side JavaScript. LinkyCal returns a hosted thank-you page by default, or a redirect if you configure one in the form builder.

HTML Form Actionhtml
<form action="https://linkycal.com/api/public/forms/acme/contact/submit" method="post">
  <input type="text" name="full_name" required />
  <input type="email" name="email" required />
  <textarea name="message"></textarea>
  <button type="submit">Send</button>
</form>
Use your form field IDs as the HTML input name values. You can get the exact IDs from the form builder or the generated form API prompt.
Native HTML submissions do not support file inputs yet. Use the widget or JSON API flow if your form includes file upload fields.

Get Form Config

GET /api/widget/form/:projectSlug/:formSlug/config

Returns the full form structure with steps, fields, and validation rules. This is the same endpoint used internally by the form widget.

Requestbash
curl "https://linkycal.com/api/widget/form/acme/contact/config"
Responsejson
{
  "form": {
    "id": "form_x1y2z3",
    "name": "Contact Form",
    "slug": "contact",
    "steps": [
      {
        "id": "step_1",
        "title": "Your Details",
        "fields": [
          { "id": "fld_1", "type": "text", "label": "Full Name", "required": true },
          { "id": "fld_2", "type": "email", "label": "Email", "required": true },
          { "id": "fld_3", "type": "phone", "label": "Phone", "required": false }
        ]
      }
    ]
  }
}

Check Availability

GET /api/v1/availability/:projectSlug

Returns available time slots for a given event type on a specific date. Use this to display available times to your users before creating a booking.

PropertyTypeRequiredDescription
datestringYesDate in YYYY-MM-DD format
timezonestringNoIANA timezone, defaults to UTC
eventTypeSlugstringYesThe event type's URL slug
Requestbash
curl -H "Authorization: Bearer lc_live_your_api_key" \
  "https://linkycal.com/api/v1/availability/acme?date=2026-03-24&timezone=UTC&eventTypeSlug=consultation"
Responsejson
{
  "slots": [
    { "start": "2026-03-24T09:00:00Z", "end": "2026-03-24T09:30:00Z" },
    { "start": "2026-03-24T10:00:00Z", "end": "2026-03-24T10:30:00Z" }
  ],
  "date": "2026-03-24",
  "timezone": "UTC"
}

Create Booking

POST /api/v1/bookings

Create a new booking for an available time slot. The system validates slot availability before confirming. A confirmation email is automatically sent to the guest.

PropertyTypeRequiredDescription
projectSlugstringYesYour project's URL slug
eventTypeSlugstringYesThe event type's URL slug
startTimestringYesISO 8601 datetime
namestringYesGuest name
emailstringYesGuest email
phonestringNoGuest phone number
notesstringNoAdditional notes from the guest
timezonestringNoGuest timezone (IANA format)
Requestbash
curl -X POST "https://linkycal.com/api/v1/bookings" \
  -H "Authorization: Bearer lc_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "projectSlug": "acme",
    "eventTypeSlug": "consultation",
    "startTime": "2026-03-24T09:00:00Z",
    "name": "Jane Smith",
    "email": "jane@example.com",
    "phone": "+1-555-0123",
    "notes": "Looking forward to our meeting",
    "timezone": "America/New_York"
  }'
Responsejson
{
  "booking": {
    "id": "bk_abc123",
    "eventTypeId": "et_xyz789",
    "name": "Jane Smith",
    "email": "jane@example.com",
    "phone": "+1-555-0123",
    "startTime": "2026-03-24T09:00:00Z",
    "endTime": "2026-03-24T09:30:00Z",
    "timezone": "America/New_York",
    "status": "confirmed",
    "notes": "Looking forward to our meeting",
    "createdAt": "2026-03-23T20:00:00Z"
  }
}
A confirmation email is automatically sent to the guest. If Google Calendar is connected, an event is created.

Cancel Booking

PATCH /api/projects/:projectId/bookings/:bookingId/cancel

Cancel an existing booking. The booking status is set to cancelled. If connected to Google Calendar, the calendar event is also removed.

This is a protected route requiring session authentication. It is not accessible via API key — use it from the dashboard or an authenticated session.
Requestbash
curl -X PATCH "https://linkycal.com/api/projects/proj_123/bookings/bk_abc123/cancel" \
  -H "Cookie: session=your_session_cookie"
Responsejson
{
  "booking": {
    "id": "bk_abc123",
    "status": "cancelled",
    "cancelledAt": "2026-03-24T08:00:00Z"
  }
}

List Contacts

GET /api/projects/:projectId/contacts

Retrieve all contacts for a project, with optional search and tag filtering.

PropertyTypeRequiredDescription
searchstringNoSearch by name or email
tagIdstringNoFilter by tag ID
Responsejson
{
  "contacts": [
    {
      "id": "ct_m1n2o3",
      "name": "Jane Smith",
      "email": "jane@example.com",
      "phone": "+1-555-0123",
      "tags": ["lead", "vip"],
      "createdAt": "2026-03-20T10:00:00Z"
    }
  ]
}

Create Contact

POST /api/projects/:projectId/contacts

Create a new contact in your project. Contacts are also created automatically when a form is submitted or a booking is made.

PropertyTypeRequiredDescription
namestringYesContact's full name
emailstringNoContact's email address
phonestringNoContact's phone number
notesstringNoInternal notes about the contact
Requestbash
curl -X POST "https://linkycal.com/api/projects/proj_123/contacts" \
  -H "Content-Type: application/json" \
  -H "Cookie: session=your_session_cookie" \
  -d '{
    "name": "Jane Smith",
    "email": "jane@example.com",
    "phone": "+1-555-0123",
    "notes": "Met at conference"
  }'
Responsejson
{
  "contact": {
    "id": "ct_p4q5r6",
    "name": "Jane Smith",
    "email": "jane@example.com",
    "phone": "+1-555-0123",
    "notes": "Met at conference",
    "createdAt": "2026-03-24T14:00:00Z"
  }
}

Update Contact

PUT /api/projects/:projectId/contacts/:contactId

Update an existing contact's information. Only provided fields are updated.

Requestbash
curl -X PUT "https://linkycal.com/api/projects/proj_123/contacts/ct_p4q5r6" \
  -H "Content-Type: application/json" \
  -H "Cookie: session=your_session_cookie" \
  -d '{
    "name": "Jane Smith-Doe",
    "notes": "Updated: now a paying customer"
  }'
Responsejson
{
  "contact": {
    "id": "ct_p4q5r6",
    "name": "Jane Smith-Doe",
    "email": "jane@example.com",
    "phone": "+1-555-0123",
    "notes": "Updated: now a paying customer",
    "updatedAt": "2026-03-24T15:00:00Z"
  }
}

Booking Widget

Embed a fully functional booking experience on any page. The widget handles event type selection, date/time picking, and form submission.

Full Embedhtml
<div id="booking-widget"></div>
<script src="https://cdn.linkycal.com/widgets/booking.js"></script>
<script>
  LinkyCal.booking({
    projectSlug: "acme",
    container: "#booking-widget",
    eventTypeSlug: "consultation",
    theme: {
      primaryColor: "#1B4332"
    }
  });
</script>
PropertyTypeRequiredDescription
projectSlugstringYesYour project's URL slug
containerstring | HTMLElementYesCSS selector or DOM element
eventTypeSlugstringNoSkip event type selection
theme.primaryColorstringNoOverride brand color

Form Widget

Embed multi-step forms directly on your site. The widget renders each step in sequence and handles validation, submission, and completion state.

Full Embedhtml
<div id="form-widget"></div>
<script src="https://cdn.linkycal.com/widgets/form.js"></script>
<script>
  LinkyCal.form({
    projectSlug: "acme",
    formSlug: "contact",
    container: "#form-widget",
    theme: {
      primaryColor: "#1B4332"
    }
  });
</script>
PropertyTypeRequiredDescription
projectSlugstringYesYour project's URL slug
formSlugstringYesThe form's URL slug
containerstring | HTMLElementYesCSS selector or DOM element
theme.primaryColorstringNoOverride brand color

Customization

Both widgets accept a theme object to match your brand. Override colors, border radius, and fonts.

Theme Overridejavascript
LinkyCal.booking({
  projectSlug: "acme",
  container: "#booking-widget",
  theme: {
    primaryColor: "#1B4332",
    borderRadius: "12px",
    fontFamily: "Inter, sans-serif"
  }
});
Both widgets are zero-dependency IIFE bundles under 6KB gzipped.

Triggers

Workflows start with a trigger. When the trigger event occurs, all connected actions execute in sequence.

form_submittedFires when a form response is completed
booking_createdFires when a new booking is confirmed
booking_cancelledFires when a booking is cancelled
tag_addedFires when a tag is added to a contact
manualTriggered manually via API

Actions

Actions are the steps executed when a workflow is triggered. Chain multiple actions together with conditions and delays.

send_emailSend email via Resend
add_tag / remove_tagModify contact tags
waitDelay execution by a specified duration
conditionIf/else branching based on contact data
webhookHTTP request to an external URL
update_contactModify contact fields

Webhook Events

When a workflow's webhook action fires, it sends a POST request to your configured URL with a JSON payload describing the event.

Webhook Payloadjson
{
  "event": "form_submitted",
  "timestamp": "2026-03-24T14:00:00Z",
  "data": {
    "formId": "form_x1y2z3",
    "responseId": "resp_a1b2c3d4",
    "contactId": "ct_m1n2o3"
  }
}

MCP Server

LinkyCal ships a built-in Model Context Protocol server, so AI agents like Claude can check availability, book meetings, manage contacts, and inspect forms on your behalf. It speaks Streamable HTTP at a single endpoint:

MCP Endpointtext
https://linkycal.com/api/mcp

Sessions authenticate with a project API key passed as a Bearer token. Every tool is hard-scoped to that key's project — agents never pass a project ID and can never reach data outside the project the key belongs to.

Connecting

Create an API key in the dashboard under MCP & APIs, then register the server with your MCP client. With Claude Code:

Claude Codebash
claude mcp add --transport http linkycal https://linkycal.com/api/mcp \
  --header "Authorization: Bearer lc_live_a1b2c3d4e5f6..."

For other MCP clients, add the server to your configuration file:

MCP Client Configjson
{
  "mcpServers": {
    "linkycal": {
      "type": "http",
      "url": "https://linkycal.com/api/mcp",
      "headers": {
        "Authorization": "Bearer lc_live_a1b2c3d4e5f6..."
      }
    }
  }
}
The API key grants full access to its project. Configure it in server-side or local agent environments only — never ship it in client-side code.
Once connected, just ask in natural language: “Book a 30-minute demo with Sarah Chen next Tuesday at 2pm” — the agent picks the right tools, checks availability, and confirms the booking.

Available Tools

The server exposes 30 tools mirroring the REST API, grouped by domain. Read tools return JSON; write tools enforce the same plan limits and validation as the dashboard.

DomainTools
Bookingslist_bookings · get_booking · get_available_slots · create_booking · cancel_booking · confirm_booking · decline_booking
Event Typeslist_event_types · get_event_type · create_event_type · update_event_type
Scheduleslist_schedules · get_schedule
Contactslist_contacts · get_contact · create_contact · update_contact · delete_contact · get_contact_activity
Tagslist_contact_tags · create_contact_tag · add_tag_to_contact · remove_tag_from_contact
Formslist_forms · get_form · create_form · update_form · list_form_responses
Workflowslist_workflows · get_workflow
Workflows are read-only over MCP by design — agents can inspect automations but not modify them. Booking writes still trigger your workflows, emails, and calendar sync exactly like the API.

API Keys

Create API keys in the dashboard under MCP & APIs. Include your key in the Authorization header as a Bearer token with every request.

Authorization Headerbash
curl -H "Authorization: Bearer lc_live_a1b2c3d4e5f6..." \
  "https://linkycal.com/api/v1/availability/your-project?date=2026-03-24&eventTypeSlug=consultation"
API keys grant full access to your project. Never expose them in client-side code.

Rate Limits

The API enforces per-IP rate limits to ensure fair usage. If you exceed the limit, you'll receive a 429 Too Many Requests response.

EndpointLimit
Availability checks60 requests/minute per IP
Booking creation10 requests/minute per IP
Form responses30 requests/minute per IP
Form step submissions60 requests/minute per IP
Rate limits apply to public API endpoints. Dashboard API endpoints have separate, higher limits.

Error Handling

All errors return a JSON object with a descriptive error field. Use the HTTP status code to determine the type of error.

Error Responsejson
{
  "error": "Descriptive error message"
}

Common status codes:

400Validation error — check your request body or parameters
401Unauthorized — missing or invalid API key
403Forbidden — plan limit reached or feature not available
404Not found — resource does not exist
429Rate limited — too many requests, try again later
500Server error — something went wrong on our end

Pagination

Currently, list endpoints return all results. Cursor-based pagination will be added in a future release with cursor and limit query parameters.

For now, all list endpoints return the full result set. Plan for pagination support if you're building against the Contacts API.

Webhooks

Configure webhooks via workflow actions to receive real-time notifications about events in your project. Create a workflow with a trigger (e.g. form_submitted) and add a webhook action pointing to your URL.

Example: Workflow with Webhookjson
{
  "trigger": "booking_created",
  "actions": [
    {
      "type": "webhook",
      "config": {
        "url": "https://your-app.com/webhooks/linkycal",
        "method": "POST",
        "headers": {
          "X-Webhook-Secret": "your_secret"
        }
      }
    }
  ]
}
Always verify webhook payloads using a shared secret to ensure requests originate from LinkyCal.

Ready to integrate?

Get your API key and start building with LinkyCal's forms, booking, and contact APIs today.