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 Widget -->
<script src="https://cdn.linkycal.com/widgets/booking.js"></script>
<script>
LinkyCal.booking({
projectSlug: "your-project",
container: "#booking-widget"
});
</script>
<!-- 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:
- Create a project in the dashboard
- Create an event type or form
- Use the anonymous visitor endpoints in your public form or booking UI
- For server-side management, create a project API key under MCP & APIs
# Check available slots
curl "https://linkycal.com/api/v1/availability/your-project?date=2026-08-12&timezone=UTC&eventTypeSlug=consultation"
How It Works
Every interaction with LinkyCal follows a straightforward request/response flow:
- Client sends request to LinkyCal API
- Server validates input and checks availability/form config
- Action is performed (booking created, form step submitted, etc.)
- Response returned with result
- Optional: workflow triggers fire (email, webhook, tag)
API Families
Visitor API · /api/v1/*
Canonical anonymous endpoints for availability, bookings, and multi-step form submissions. These routes are safe for visitor-side code and are rate limited by IP.
Share-link API · /api/public/*
Public link resolution and form configuration used by LinkyCal share pages. The response-creation and step-submission variants are retained for compatibility; new direct integrations should prefer /api/v1/*.
Management API · /api/projects/:projectId/*
Server-side project administration with a project-scoped API key. Never expose this credential in a browser, widget, or public form.
Endpoint Catalog
Anonymous visitor endpoints cover form responses, file uploads, availability, booking creation, widgets, and public link resolution. They are rate limited and need no credential. Never put an API key in visitor-side code.
The protected management API uses the canonical /api/projects/:projectId/* routes. Send Authorization: Bearer lc_live_... from a server, secure automation, or local agent. The key must belong to the project ID in the URL and the project must have API access on its current plan.
Dashboard-only Operations
Project creation and deletion, members, API-key management, billing, and OAuth connections are dashboard-only. Account, team, and onboarding operations are also session-only. API keys receive api_key_route_forbidden for these operations; use the dashboard instead.
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.
curl -X POST "https://linkycal.com/api/v1/forms/acme/contact/responses" \
-H "Content-Type: application/json"
{
"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...). Pass complete: true on the final visible step to mark the response completed.
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" }
]
}'
{
"response": {
"id": "resp_a1b2c3d4",
"status": "in_progress",
"currentStepIndex": 1,
"completedAt": null
}
}
Upload File
POST /api/v1/forms/:projectSlug/:formSlug/responses/:responseId/uploads
Upload a file for a file field before submitting that step through the JSON API. The upload response returns a private file pointer that you include as fileUrl.
curl -X POST "https://linkycal.com/api/v1/forms/acme/contact/responses/resp_a1b2c3d4/uploads" \
-F "fieldId=resume" \
-F "file=@./resume.pdf"
curl -X PATCH "https://linkycal.com/api/v1/forms/acme/contact/responses/resp_a1b2c3d4/steps/0" \
-H "Content-Type: application/json" \
-d '{
"fields": [
{
"fieldId": "resume",
"value": "resume.pdf",
"fileUrl": "form-responses/project/form/response/resume/upload-id.pdf"
}
]
}'
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.
<form action="https://linkycal.com/api/public/forms/acme/contact/submit" method="post" enctype="multipart/form-data">
<input type="text" name="full_name" required />
<input type="email" name="email" required />
<input type="file" name="resume" />
<textarea name="message"></textarea>
<button type="submit">Send</button>
</form>
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.
curl "https://linkycal.com/api/widget/form/acme/contact/config"
{
"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 }
]
}
]
}
}
Other visitor endpoints
Supporting anonymous routes used by public links, widgets, analytics, and older form integrations.
/api/v1/event-types/:projectSlug/:eventSlug
Get public event type configuration.
/api/v1/t
Record an anonymous visitor analytics event.
/api/public/resolve/:projectSlug/:slug
Resolve a share-link slug to a form or event type.
/api/public/forms/:projectSlug/:formSlug/responses
Legacy form response creation.
/api/public/forms/:projectSlug/:formSlug/responses/:responseId/steps/:stepIndex
Legacy form step submission.
/api/public/forms/:projectSlug/:formSlug/submit
Legacy single-request form submission.
/api/uploads/:key
Download a public upload by object key.
- The /api/public form write routes are retained for compatibility. New direct integrations should use the equivalent /api/v1 form routes.
- Public upload URLs contain unguessable object keys; do not treat them as authorization for sensitive private response files.
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.
curl "https://linkycal.com/api/v1/availability/acme?date=2026-08-12&timezone=UTC&eventTypeSlug=consultation"
{
"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.
curl -X POST "https://linkycal.com/api/v1/bookings" \
-H "Content-Type: application/json" \
-d '{
"projectSlug": "acme",
"eventTypeSlug": "consultation",
"startTime": "2026-03-24T09:00:00Z",
"name": "Jane Smith",
"email": "jane@example.com",
"notes": "Looking forward to our meeting",
"timezone": "America/New_York"
}'
{
"booking": {
"id": "bk_abc123",
"eventTypeId": "et_xyz789",
"name": "Jane Smith",
"email": "jane@example.com",
"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"
}
}
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.
curl -X PATCH "https://linkycal.com/api/projects/proj_123/bookings/bk_abc123/cancel" \
-H "Authorization: Bearer lc_live_your_api_key"
{
"booking": {
"id": "bk_abc123",
"status": "cancelled",
"cancelledAt": "2026-03-24T08:00:00Z"
}
}
List Contacts
GET /api/projects/:projectId/contacts
Retrieve the first page of contacts for a project, with optional search and tag filtering. The response includes the full filtered total; see Contact list filters for the complete filter and pagination reference.
{
"contacts": [
{
"id": "ct_m1n2o3",
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "+1-555-0123",
"tags": [
{ "id": "tag_lead", "name": "Lead", "color": "#1B4332" }
],
"lastActivityAt": "2026-07-22T08:30:00.000Z",
"createdAt": "2026-03-20T10:00:00.000Z"
}
],
"total": 1
}
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.
curl -X POST "https://linkycal.com/api/projects/proj_123/contacts" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lc_live_your_api_key" \
-d '{
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "+1-555-0123",
"notes": "Met at conference"
}'
{
"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.
curl -X PUT "https://linkycal.com/api/projects/proj_123/contacts/ct_p4q5r6" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer lc_live_your_api_key" \
-d '{
"name": "Jane Smith-Doe",
"notes": "Updated: now a paying customer"
}'
{
"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"
}
}
Projects and entitlements
Read and update the project addressed by the API key, and inspect the plan limits that apply to it.
/api/projects/:projectId
Get the current project.
/api/projects/:projectId
Update project settings and branding.
/api/projects/:projectId/entitlements
Get the subscription, usage, and effective plan limits.
- Project creation and deletion remain dashboard-only.
- PUT accepts name, slug, timezone, and onboarded; slugs use lowercase letters, digits, and hyphens.
- Entitlements are the source of truth for API access and resource limits; do not hard-code plan assumptions.
Event types
Manage bookable meeting definitions and choose which connected calendars each event type reads and writes.
/api/projects/:projectId/event-types
List event types.
/api/projects/:projectId/event-types
Create an event type.
/api/projects/:projectId/event-types/:eventTypeId
Get an event type.
/api/projects/:projectId/event-types/:eventTypeId
Update an event type.
/api/projects/:projectId/event-types/:eventTypeId
Delete an event type.
/api/projects/:projectId/event-types/:eventTypeId/calendars
Get destination and availability calendar selections.
/api/projects/:projectId/event-types/:eventTypeId/calendars
Replace destination and availability calendar selections.
- Event type writes are subject to the project plan limit.
- Create with name, slug, duration, and optional description, location, color, buffers, booking caps, confirmation mode, bookingFormId, settings, or copyFromEventTypeId.
- Calendar IDs must belong to a connection available to the project or its team.
Schedules and availability rules
Manage reusable schedules, their weekly working windows, and date-specific availability overrides.
/api/projects/:projectId/schedules
List schedules.
/api/projects/:projectId/schedules
Create a schedule.
/api/projects/:projectId/schedules/:scheduleId
Update a schedule.
/api/projects/:projectId/schedules/:scheduleId
Delete a schedule.
/api/projects/:projectId/schedules/:scheduleId/rules
List weekly availability rules.
/api/projects/:projectId/schedules/:scheduleId/rules
Replace weekly availability rules.
/api/projects/:projectId/schedules/:scheduleId/overrides
List date overrides.
/api/projects/:projectId/schedules/:scheduleId/overrides
Create a date override.
/api/projects/:projectId/schedules/:scheduleId/overrides/:overrideId
Delete a date override.
- Times in rules and overrides are local to the schedule timezone.
- Create schedules with name, timezone, and isDefault. Rules contain dayOfWeek, startTime, and endTime; 24:00 is valid only as an end time.
- Replacing rules uses the complete submitted rule set; read the current rules before editing.
Booking management
Read bookings created by visitor flows and perform organizer-side state transitions.
/api/projects/:projectId/bookings
List project bookings.
/api/projects/:projectId/bookings/:bookingId
Get a booking.
/api/projects/:projectId/bookings/:bookingId/cancel
Cancel a booking and optionally record a reason.
/api/projects/:projectId/bookings/:bookingId/confirm
Confirm a pending booking.
/api/projects/:projectId/bookings/:bookingId/decline
Decline a pending booking.
/api/projects/:projectId/bookings/:bookingId/form-response
Get the intake form response attached to a booking.
- Confirmation and cancellation may update Google Calendar and send email.
- Cancellation accepts an optional reason. Decline accepts an optional reason and notify flag; confirmation requires no request body.
- The visitor booking creation endpoint is documented separately under Booking API.
Forms and responses
Manage forms, ordered steps and fields, submitted responses, and private response files.
/api/projects/:projectId/forms
List forms.
/api/projects/:projectId/forms
Create a form.
/api/projects/:projectId/forms/:formId
Get a form with its structure.
/api/projects/:projectId/forms/:formId
Update a form.
/api/projects/:projectId/forms/:formId
Delete a form.
/api/projects/:projectId/forms/:formId/steps
List form steps.
/api/projects/:projectId/forms/:formId/steps
Create a form step.
/api/projects/:projectId/forms/:formId/steps/:stepId
Update a form step.
/api/projects/:projectId/forms/:formId/steps/:stepId
Delete a form step.
/api/projects/:projectId/forms/:formId/steps/reorder
Reorder form steps.
/api/projects/:projectId/forms/:formId/fields
List form fields.
/api/projects/:projectId/forms/:formId/fields
Create a form field.
/api/projects/:projectId/forms/:formId/fields/:fieldId
Update a form field.
/api/projects/:projectId/forms/:formId/fields/:fieldId
Delete a form field.
/api/projects/:projectId/forms/:formId/fields/reorder
Reorder form fields.
/api/projects/:projectId/forms/:formId/responses
List form responses.
/api/projects/:projectId/forms/:formId/responses/:responseId
Get a form response.
/api/projects/:projectId/forms/:formId/responses/:responseId/files/:valueId
Download a private response file.
/api/v1/forms/:projectSlug/:formSlug/responses/:responseId/files/:valueId
Download a private response file through the project-scoped API-key route.
/api/projects/:projectId/form-responses/:responseId
Delete a form response.
- Reorder endpoints accept the complete ordered list of IDs.
- Forms use name, slug, type, status, and settings. Steps contain display and visibility data; fields contain type, label, validation, options, contact mapping, and visibility.
- Deleting a response removes its stored values and owned response files.
Contact management
Manage CRM records, saved list and Kanban views, activity, next actions, stages, imports, and enrichment.
/api/projects/:projectId/contacts
List and filter contacts.
/api/projects/:projectId/contacts
Create a contact.
/api/projects/:projectId/contacts/import
Import mapped contact rows.
/api/projects/:projectId/contacts/:contactId
Get a contact with tags.
/api/projects/:projectId/contacts/:contactId
Update a contact.
/api/projects/:projectId/contacts/:contactId
Delete a contact.
/api/projects/:projectId/contacts/:contactId/activities
List the cursor-paginated contact timeline and category counts.
/api/projects/:projectId/contacts/:contactId/next-action
Set, replace, or complete the contact's next action.
/api/projects/:projectId/contacts/:contactId/stage
Move a contact between stage tags.
/api/projects/:projectId/contacts/:contactId/enrich
Run contact research and return the enriched contact.
/api/projects/:projectId/contact-views
List saved contact views.
/api/projects/:projectId/contact-views
Create a saved contact view.
/api/projects/:projectId/contact-views/:viewId
Update a saved contact view.
/api/projects/:projectId/contact-views/:viewId
Delete a saved contact view.
/api/projects/:projectId/pipeline/seed
Create the default pipeline stages when missing.
- Contact listing defaults to 50 records and returns { contacts, total }; use limit and offset to load more.
- Supported filters include search, tagId, repeated tagIds, matchAllTags, stageTagId, repeated excludeStageTagIds, activityType, activitySinceDays, noActivitySinceDays, and bookingStatus.
- Contact writes support name, email, phone, notes, metadata, company, companyWebsite, position, companySize, estimatedRevenue, and linkedinUrl. Set next action with { text, deadline }; send both as null to complete it.
Contact list filters
curl --get "https://linkycal.com/api/projects/proj_123/contacts" \
-H "Authorization: Bearer lc_live_your_api_key" \
--data-urlencode "tagIds=tag_lead" \
--data-urlencode "tagIds=tag_vip" \
--data-urlencode "matchAllTags=true" \
--data-urlencode "limit=50" \
--data-urlencode "offset=0"
Contact activity pagination
Activity accepts category values all, bookings, form_responses, or workflows. The default page size is 20 and the maximum is 100. Pass the returned opaque nextCursor unchanged to load the next page.
curl --get "https://linkycal.com/api/projects/proj_123/contacts/ct_123/activities" \
-H "Authorization: Bearer lc_live_your_api_key" \
--data-urlencode "category=bookings" \
--data-urlencode "limit=20"
Tags
Create project tags and assign them to contacts. Names are unique within a project after trimming and case normalization.
/api/projects/:projectId/tags
List tags with search and cursor pagination.
/api/projects/:projectId/tags
Create a tag from name and optional #RRGGBB color.
/api/projects/:projectId/tags/:tagId
Get a tag.
/api/projects/:projectId/tags/:tagId
Update a tag's name or color.
/api/projects/:projectId/tags/:tagId
Delete an unused tag.
/api/projects/:projectId/contacts/:contactId/tags
Legacy assignment endpoint accepting { tagId }.
/api/projects/:projectId/contacts/:contactId/tags/:tagId
Assign a tag to a contact idempotently.
/api/projects/:projectId/contacts/:contactId/tags/:tagId
Remove a tag from a contact idempotently.
- List tags with search and optional cursor pagination. A cursor requires the same limit on the next request.
- PUT assignment and DELETE removal are idempotent: assigned or removed is false when no relationship changed.
- Deleting a workflow-referenced tag returns 409 TAG_IN_USE with the workflows that block deletion.
- POST assignment is retained for compatibility; new integrations should use the canonical PUT endpoint.
List and create tags
curl -X POST "https://linkycal.com/api/projects/proj_123/tags" \
-H "Authorization: Bearer lc_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"name":"Qualified lead","color":"#1B4332"}'
{
"tags": [
{
"id": "tag_123",
"projectId": "proj_123",
"name": "Qualified lead",
"color": "#1B4332",
"createdAt": "2026-07-22T09:00:00.000Z"
}
],
"nextCursor": null
}
Assign and remove contact tags
curl -X PUT \
"https://linkycal.com/api/projects/proj_123/contacts/ct_123/tags/tag_123" \
-H "Authorization: Bearer lc_live_your_api_key"
Assignment returns { tag, assigned }; removal returns { success, tag, removed }. Repeating either request succeeds and reports false for the unchanged relationship.
{
"error": "Tag is referenced by one or more workflows",
"code": "TAG_IN_USE",
"workflows": [{ "id": "wf_123", "name": "Research new leads" }]
}
Workflow management
Manage workflow definitions and ordered steps, inspect execution runs, and start manual or test executions.
/api/projects/:projectId/workflows
List workflows.
/api/projects/:projectId/workflows
Create a workflow.
/api/projects/:projectId/workflows/:workflowId
Get a workflow.
/api/projects/:projectId/workflows/:workflowId
Update a workflow.
/api/projects/:projectId/workflows/:workflowId
Delete a workflow.
/api/projects/:projectId/workflows/:workflowId/steps
List workflow steps.
/api/projects/:projectId/workflows/:workflowId/steps
Create a workflow step.
/api/projects/:projectId/workflows/:workflowId/steps/:stepId
Update a workflow step.
/api/projects/:projectId/workflows/:workflowId/steps/:stepId
Delete a workflow step.
/api/projects/:projectId/workflows/:workflowId/steps/reorder
Reorder workflow steps.
/api/projects/:projectId/workflows/:workflowId/runs
List workflow runs with an optional limit.
/api/projects/:projectId/workflows/:workflowId/runs/:runId
Get one run with step snapshots.
/api/projects/:projectId/workflows/:workflowId/trigger
Start a manual workflow run.
/api/projects/:projectId/workflows/:workflowId/test
Test a workflow against supplied context.
- Triggers: form_submitted, booking_created, booking_cancelled, booking_pending, booking_confirmed, new_contact_created, tag_added, manual, and scheduled.
- Step types: send_email, ai_research, add_tag, remove_tag, wait, condition, webhook, and update_contact.
- Create a workflow with name, trigger, and optional triggerConfig. Steps use sortOrder, type, config, and an optional condition; update calls are partial.
Analytics and recent activity
Read project-level activity and unique-journey booking/form funnels, then configure validated customer analytics providers.
/api/projects/:projectId/activity/recent
Get recent project activity.
/api/projects/:projectId/analytics/filters
Get available analytics filter values.
/api/projects/:projectId/analytics/overview
Get conversion and traffic overview metrics.
/api/projects/:projectId/analytics/bookings
Get booking analytics.
/api/projects/:projectId/analytics/forms
Get form analytics.
/api/projects/:projectId/analytics/integrations
List normalized GA4, Meta Pixel, and PostHog configuration.
/api/projects/:projectId/analytics/integrations/:provider
Configure one allowlisted analytics provider.
- Detailed analytics and provider configuration require a Pro or Business project. The Worker enforces entitlement and project scope for sessions, API keys, and MCP.
- Analytics supports period=7d|30d|90d|custom. Custom requires both inclusive start and end ISO dates; preset periods omit them.
- Queries may filter by resourceSlug, UTM source/medium/campaign, direct or widget source, and mobile/tablet/desktop device type.
- Choose one project-owned event type or form for exact stages. All resources remains a backward-compatible high-level summary.
- Detailed reports count unique journeys and expose availableSince, stage visitors, continuation/drop-off rates, conditional skips, booking date/time availability context, and safe failures.
- Reports never return names, emails, raw answers, journey IDs, IP addresses, or raw errors.
- Provider writes accept only public GA4 measurement IDs, numeric Meta Pixel IDs, and PostHog project keys with an allowlisted US/EU host. Raw scripts and arbitrary URLs are rejected.
Files and project calendars
Upload project-owned files and inspect calendars available through the project's connected Google accounts.
/api/projects/:projectId/uploads
Upload a project-owned file.
/api/projects/:projectId/uploads/:key
Delete a project-owned upload.
/api/projects/:projectId/calendar/calendars
List calendars available to the project.
- Uploads use multipart/form-data and return an object key or URL for later use.
- Deleting an upload requires the complete object key, including nested path segments.
- Calendar listing is read-only; connect or disconnect Google accounts in the dashboard, then select calendars through the event type endpoints.
Booking Widget
Embed a fully functional booking experience on any page. The widget handles event type selection, date/time picking, and form submission.
<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>
Form Widget
Embed multi-step forms directly on your site. The widget renders each step in sequence and handles validation, submission, and completion state.
<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>
Customization
Both widgets accept a theme object to match your brand. Override colors, border radius, and fonts.
LinkyCal.booking({
projectSlug: "acme",
container: "#booking-widget",
theme: {
primaryColor: "#1B4332",
borderRadius: "12px",
fontFamily: "Inter, sans-serif"
}
});
Triggers
Workflows start with a trigger. When the trigger event occurs, all connected actions execute in sequence.
Actions
Actions are the steps executed when a workflow is triggered. Chain multiple actions together with conditions and delays.
Webhook Events
A webhook step sends the configured method, headers, and body to an external URL. The method defaults to POST; the body defaults to the workflow context below, but you can replace it and interpolate workflow values.
{
"projectId": "proj_123",
"contactId": "ct_m1n2o3",
"contactEmail": "jane@example.com",
"formResponseId": "resp_a1b2c3d4",
"metadata": {}
}
MCP Server
LinkyCal ships a built-in Model Context Protocol server, so AI agents can check availability, book meetings, manage contacts, and inspect forms on your behalf. It speaks Streamable HTTP at a single endpoint:
https://linkycal.com/api/mcp
MCP connections 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 using the endpoint and authorization header below.
{
"mcpServers": {
"linkycal": {
"type": "http",
"url": "https://linkycal.com/api/mcp",
"headers": {
"Authorization": "Bearer lc_live_a1b2c3d4e5f6..."
}
}
}
}
Available Tools
The server exposes 40 tools for the project, grouped by domain. Read tools return JSON; write tools enforce the same plan limits and validation as the dashboard.
get_booking_funnel_analytics and get_form_funnel_analytics return unique-journey stage visitors, continuation, drop-offs, skips, safe context distributions, and the detailed-data boundary. Aggregate outputs exclude names, emails, raw answers, journey IDs, IP addresses, and raw errors. configure_analytics_integration accepts only GA4, Meta Pixel, or PostHog public identifiers and an allowlisted US/EU PostHog host.
API Keys
Create API keys in the dashboard under MCP & APIs. Include your key in the Authorization header as a Bearer token for MCP and protected /api/projects/:projectId/* requests. Visitor endpoints are anonymous and must not receive this header.
curl -H "Authorization: Bearer lc_live_a1b2c3d4e5f6..." \
"https://linkycal.com/api/projects/proj_123/contacts"
API access is available on Pro and Business projects. Free projects return api_access_unavailable even when the key itself is valid. Read GET /api/projects/:projectId/entitlements when your integration needs the current resource limits.
Send either a dashboard session or an API key, never both. If a request has a valid session plus any Authorization header, LinkyCal returns ambiguous_credentials. An invalid Bearer value returns invalid_api_key and never falls back to a session. A key used with another project returns api_key_project_mismatch; a project without current API entitlement returns api_access_unavailable.
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.
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": "Descriptive error message"
}
Common status codes:
Pagination
Pagination is endpoint-specific. Do not assume that every list uses the same model.
Contacts
Offset pagination with limit and offset. The default limit is 50, the maximum is 100, and the response includes the full filtered total.
Contact activity
Cursor pagination with a default limit of 20 and maximum of 100. Pass the returned opaque nextCursor unchanged.
Tags
Optional cursor pagination from 1 to 100 items. When using cursor, repeat the same limit on the next request.
Other lists
Other list operations currently return their complete result unless their section or OpenAPI operation declares a limit.
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.
{
"trigger": "booking_created",
"actions": [
{
"type": "webhook",
"config": {
"url": "https://your-app.com/webhooks/linkycal",
"method": "POST",
"headers": {
"X-Webhook-Secret": "your_secret"
}
}
}
]
}
Ready to integrate?
Build visitor forms and booking flows without a secret, then use a project key for trusted management integrations.