REST API reference

Fresha API

14 endpoints for Fresha. Each one is a POST with a JSON body, called with your API key.

Base URL
https://cotlet.dev/api/v1/fresha
Connects with
Your own Fresha business account (owner, or a team member whose permissions cover the calendar, clients, reports and wallet)
Integration page
Fresha
Fresha Endpoints
Endpoint Access Description
list_appointments read Appointments in a date range, with client, services, team member and status.
find_available_slots read Free start times on one day for one or more services, for anyone or one team member.
list_clients read Your client list with contact details, last appointment and marketing consent.
get_client read Client profile: details, notes, allergies, and upcoming and past appointments.
list_services read Your service menu with categories, durations and prices.
list_team_members read Bookable team members with their locations and services.
get_sales_summary read Sales, discounts and refunds from the Sales summary report, for a date range.
list_payouts read Payouts from your business wallet to your bank, with dates, amounts and fees.
create_appointment write Book one or more services for a client at a given time.
reschedule_appointment write Move an appointment to a new time, and optionally to another team member.
cancel_appointment write Cancel a current or future appointment with one of your cancellation reasons.
add_client write Create a client profile. Nothing is sent to the client.
add_client_note write Save a note on a client profile, such as a color formula or preference.
add_blocked_time write Block time in a team member's calendar for a break, training or admin.

POST https://cotlet.dev/api/v1/fresha/list_appointments

Lists appointments for the dates you give, ordered by start time. Each one has its client, services, team member and status. Statuses are Booked, Confirmed, Completed, Cancelled and No-show, plus any custom statuses your business created in Settings; custom ones come back under their own name. Pass appointment_id from an item to reschedule_appointment or cancel_appointment. Pass next_cursor back as cursor for the next page.

Parameters

Parameters: list_appointments
NameTypeDescription
from required date First day.
to date Last day. Default: same as from.
status string booked, confirmed, completed, cancelled or no_show, or the name of a custom status. Default: all.
team_member_id string Only this team member.
client_id string Only this client.
location_id string Only this location. Default: all locations.
cursor string Next page cursor from the previous call.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/list_appointments \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from":"2026-10-01"}'
Python
result = client.call("fresha", "list_appointments", {"from":"2026-10-01"})
Node.js
const result = await cotlet.call('fresha', 'list_appointments', {"from":"2026-10-01"});
CLI
cotlet call fresha list_appointments --data '{"from":"2026-10-01"}'

Example response

{
  "appointments": [
    {
      "appointment_id": "ap_90311301",
      "client": {
        "client_id": "cl_48213977",
        "name": "Anna Smith"
      },
      "start_at": "2026-10-15T10:00:00+01:00",
      "end_at": "2026-10-15T10:45:00+01:00",
      "services": [
        "Haircut"
      ],
      "team_member": {
        "team_member_id": "tm_5521",
        "name": "Sam"
      },
      "status": "Booked",
      "location_id": "loc_201"
    }
  ],
  "next_cursor": null
}

POST https://cotlet.dev/api/v1/fresha/find_available_slots

Returns the free start times on one day for the services you list, booked back to back in that order. Each slot names the team member who is free. Pass start_at from a slot to create_appointment or reschedule_appointment. The search covers one day per call.

Parameters

Parameters: find_available_slots
NameTypeDescription
service_ids required string[] Services from list_services, booked in this order.
date required date Day to search.
team_member_id string Only this team member. Default: anyone.
location_id string Only this location. Default: all locations.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/find_available_slots \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"service_ids":["service ids"],"date":"2026-10-01"}'
Python
result = client.call("fresha", "find_available_slots", {"service_ids":["service ids"],"date":"2026-10-01"})
Node.js
const result = await cotlet.call('fresha', 'find_available_slots', {"service_ids":["service ids"],"date":"2026-10-01"});
CLI
cotlet call fresha find_available_slots --data '{"service_ids":["service ids"],"date":"2026-10-01"}'

Example response

{
  "date": "2026-10-16",
  "slots": [
    {
      "start_at": "2026-10-16T14:30:00+01:00",
      "end_at": "2026-10-16T15:15:00+01:00",
      "team_member_id": "tm_5521",
      "team_member": "Sam"
    },
    {
      "start_at": "2026-10-16T16:00:00+01:00",
      "end_at": "2026-10-16T16:45:00+01:00",
      "team_member_id": "tm_5521",
      "team_member": "Sam"
    }
  ]
}

POST https://cotlet.dev/api/v1/fresha/list_clients

Lists clients, A to Z, with contact details, their last appointment date and marketing consent. The last appointment ignores cancelled and no-show bookings, the same as Fresha's Client insights report. Use last_appointment_before to find clients who have not been in since a date. Card details are never returned. Pages hold up to 500 clients; pass next_cursor back as cursor.

Parameters

Parameters: list_clients
NameTypeDescription
search string Name, email or phone.
last_appointment_before date Only clients whose last appointment was before this date.
location_id string Only this location. Default: all locations.
limit integer 1 to 500. Default: 100.
cursor string Next page cursor from the previous call.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/list_clients \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("fresha", "list_clients")
Node.js
const result = await cotlet.call('fresha', 'list_clients');
CLI
cotlet call fresha list_clients

Example response

{
  "clients": [
    {
      "client_id": "cl_48213977",
      "name": "Anna Smith",
      "email": "anna@example.com",
      "phone": "+44 7700 900123",
      "last_appointment": "2026-06-20",
      "accepts_marketing": true,
      "accepts_sms_marketing": false
    }
  ],
  "next_cursor": "eyJvZmZzZXQiOjEwMH0"
}

Get client

read

POST https://cotlet.dev/api/v1/fresha/get_client

Returns one client's profile: contact details, the notes your team saved, allergies, and upcoming and past appointments, newest first. Notes are visible only to your team. Use it before booking or writing to a client.

Parameters

Parameters: get_client
NameTypeDescription
client_id required string Client ID from list_clients.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/get_client \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"client_id":"client_8c41d2"}'
Python
result = client.call("fresha", "get_client", {"client_id":"client_8c41d2"})
Node.js
const result = await cotlet.call('fresha', 'get_client', {"client_id":"client_8c41d2"});
CLI
cotlet call fresha get_client --data '{"client_id":"client_8c41d2"}'

Example response

{
  "client_id": "cl_48213977",
  "name": "Anna Smith",
  "phone": "+44 7700 900123",
  "notes": [
    {
      "text": "Prefers a warm toner, no fragrance.",
      "created_at": "2026-05-14T11:02:00+01:00"
    }
  ],
  "allergies": [],
  "upcoming_appointments": [
    {
      "appointment_id": "ap_90311245",
      "start_at": "2026-10-16T14:30:00+01:00",
      "services": [
        "Haircut"
      ]
    }
  ],
  "past_appointments": [
    {
      "appointment_id": "ap_88120411",
      "start_at": "2026-06-20T11:00:00+01:00",
      "services": [
        "Colour"
      ],
      "status": "Completed"
    }
  ]
}

POST https://cotlet.dev/api/v1/fresha/list_services

Returns your service menu grouped by category, with duration and price for each service. Prices are in your business currency. Use service_id from an item in find_available_slots and create_appointment.

Parameters

Parameters: list_services
NameTypeDescription
location_id string Only this location. Default: all locations.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/list_services \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("fresha", "list_services")
Node.js
const result = await cotlet.call('fresha', 'list_services');
CLI
cotlet call fresha list_services

Example response

{
  "services": [
    {
      "service_id": "sv_1203",
      "name": "Haircut",
      "category": "Hair",
      "duration_minutes": 45,
      "price": "45.00",
      "currency": "GBP"
    },
    {
      "service_id": "sv_1288",
      "name": "Colour",
      "category": "Hair",
      "duration_minutes": 90,
      "price": "85.00",
      "currency": "GBP"
    }
  ]
}

POST https://cotlet.dev/api/v1/fresha/list_team_members

Lists the team members who can be booked, with their locations and the services each one offers. Use team_member_id from an item to filter list_appointments or to book with a specific person.

Parameters

Parameters: list_team_members
NameTypeDescription
location_id string Only this location. Default: all locations.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/list_team_members \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("fresha", "list_team_members")
Node.js
const result = await cotlet.call('fresha', 'list_team_members');
CLI
cotlet call fresha list_team_members

Example response

{
  "team_members": [
    {
      "team_member_id": "tm_5521",
      "name": "Sam",
      "locations": [
        "loc_201"
      ],
      "service_ids": [
        "sv_1203",
        "sv_1288"
      ]
    }
  ]
}

POST https://cotlet.dev/api/v1/fresha/get_sales_summary

Returns the Sales summary report for the dates you give: items sold, sales, discounts and refunds. Set group_by to split the totals by payment method, discount name or discount type. Tips and service charges are not in this report, because it is built from sale items. Amounts are in your business currency.

Parameters

Parameters: get_sales_summary
NameTypeDescription
from required date First day.
to date Last day. Default: same as from.
group_by enum payment_method, discount_name or discount_type. Default: no grouping.
location_id string Only this location. Default: all locations.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/get_sales_summary \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from":"2026-10-13","to":"2026-10-13","group_by":"payment_method","location_id":"loc_201"}'
Python
result = client.call("fresha", "get_sales_summary", {"from":"2026-10-13","to":"2026-10-13","group_by":"payment_method","location_id":"loc_201"})
Node.js
const result = await cotlet.call('fresha', 'get_sales_summary', {"from":"2026-10-13","to":"2026-10-13","group_by":"payment_method","location_id":"loc_201"});
CLI
cotlet call fresha get_sales_summary --data '{"from":"2026-10-13","to":"2026-10-13","group_by":"payment_method","location_id":"loc_201"}'

Example response

{
  "from": "2026-10-10",
  "to": "2026-10-10",
  "currency": "GBP",
  "groups": [
    {
      "payment_method": "Card",
      "items_sold": 18,
      "sales": "742.00",
      "discounts": "30.00",
      "refunds": "0.00"
    },
    {
      "payment_method": "Cash",
      "items_sold": 7,
      "sales": "266.00",
      "discounts": "10.00",
      "refunds": "45.00"
    }
  ],
  "total": {
    "items_sold": 25,
    "sales": "1008.00",
    "discounts": "40.00",
    "refunds": "45.00"
  }
}

POST https://cotlet.dev/api/v1/fresha/list_payouts

Lists transfers from your business wallet to your bank, newest first. Each has its date, amount, the fees Fresha deducted and the net amount. Scheduled and manual payouts are both included. It works only when Fresha Payments is on, and the account needs the Wallet and card processing permission.

Parameters

Parameters: list_payouts
NameTypeDescription
from date Start of the period. Default: 30 days ago.
to date End of the period. Default: today.
cursor string Next page cursor from the previous call.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/list_payouts \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("fresha", "list_payouts")
Node.js
const result = await cotlet.call('fresha', 'list_payouts');
CLI
cotlet call fresha list_payouts

Example response

{
  "payouts": [
    {
      "payout_id": "po_77120",
      "type": "scheduled",
      "date": "2026-09-29",
      "amount": "840.00",
      "fees": "12.60",
      "net": "827.40",
      "currency": "GBP"
    },
    {
      "payout_id": "po_76988",
      "type": "manual",
      "date": "2026-09-22",
      "amount": "300.00",
      "fees": "0.50",
      "net": "299.50",
      "currency": "GBP"
    }
  ],
  "next_cursor": null
}

Create appointment

write Confirm

POST https://cotlet.dev/api/v1/fresha/create_appointment

Books the services you list for a client, back to back, from start_at. The appointment starts as Booked. Without team_member_id it goes to the first team member who is free. Check the time with find_available_slots first; a time that is taken fails. The booking can reach the client. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: create_appointment
NameTypeDescription
client_id required string Client ID from list_clients.
start_at required datetime ISO 8601 with time zone.
service_ids required string[] Services from list_services, in order.
team_member_id string Team member. Default: first available.
location_id string Only this location. Default: all locations.
note string Appointment note for your team.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/create_appointment \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"client_id":"cl_48213977","start_at":"2026-10-16T14:30:00+01:00","service_ids":["sv_1203"],"team_member_id":"tm_5521"}'
Python
result = client.call("fresha", "create_appointment", {"client_id":"cl_48213977","start_at":"2026-10-16T14:30:00+01:00","service_ids":["sv_1203"],"team_member_id":"tm_5521"})
Node.js
const result = await cotlet.call('fresha', 'create_appointment', {"client_id":"cl_48213977","start_at":"2026-10-16T14:30:00+01:00","service_ids":["sv_1203"],"team_member_id":"tm_5521"});
CLI
cotlet call fresha create_appointment --data '{"client_id":"cl_48213977","start_at":"2026-10-16T14:30:00+01:00","service_ids":["sv_1203"],"team_member_id":"tm_5521"}'

Example response

{
  "appointment_id": "ap_90311245",
  "status": "Booked",
  "start_at": "2026-10-16T14:30:00+01:00",
  "end_at": "2026-10-16T15:15:00+01:00",
  "services": [
    {
      "service_id": "sv_1203",
      "name": "Haircut",
      "price": "45.00",
      "currency": "GBP"
    }
  ],
  "team_member": "Sam"
}

Reschedule appointment

write Confirm

POST https://cotlet.dev/api/v1/fresha/reschedule_appointment

Moves one appointment to a new start time, keeping its services. Set team_member_id to hand it to someone else. Check the new time with find_available_slots first. The move can reach the client. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: reschedule_appointment
NameTypeDescription
appointment_id required string Appointment ID from list_appointments.
start_at required datetime New start, ISO 8601 with time zone.
team_member_id string New team member. Default: unchanged.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/reschedule_appointment \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"appointment_id":"ap_90311301","start_at":"2026-10-15T10:00:00+01:00","team_member_id":"tm_5530"}'
Python
result = client.call("fresha", "reschedule_appointment", {"appointment_id":"ap_90311301","start_at":"2026-10-15T10:00:00+01:00","team_member_id":"tm_5530"})
Node.js
const result = await cotlet.call('fresha', 'reschedule_appointment', {"appointment_id":"ap_90311301","start_at":"2026-10-15T10:00:00+01:00","team_member_id":"tm_5530"});
CLI
cotlet call fresha reschedule_appointment --data '{"appointment_id":"ap_90311301","start_at":"2026-10-15T10:00:00+01:00","team_member_id":"tm_5530"}'

Example response

{
  "appointment_id": "ap_90311301",
  "status": "Booked",
  "start_at": "2026-10-16T10:00:00+01:00",
  "end_at": "2026-10-16T10:45:00+01:00",
  "team_member": "Sam"
}

Cancel appointment

write Confirm

POST https://cotlet.dev/api/v1/fresha/cancel_appointment

Cancels one appointment. Fresha only cancels appointments that start in the future, so a past one fails; mark it as a no-show in the app instead. The reason must be one of the cancellation reasons you manage in Fresha. Your payment policy for the appointment applies as it does in the app. For a repeating series, scope picks this appointment or the whole series. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: cancel_appointment
NameTypeDescription
appointment_id required string Appointment ID from list_appointments.
reason required string Name of one of your cancellation reasons.
notify_client boolean Send the client a cancellation message. Default: true.
scope enum this_appointment or whole_series, for repeating appointments. Default: this_appointment.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/cancel_appointment \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"appointment_id":"appointment_8c41d2","reason":"reason"}'
Python
result = client.call("fresha", "cancel_appointment", {"appointment_id":"appointment_8c41d2","reason":"reason"})
Node.js
const result = await cotlet.call('fresha', 'cancel_appointment', {"appointment_id":"appointment_8c41d2","reason":"reason"});
CLI
cotlet call fresha cancel_appointment --data '{"appointment_id":"appointment_8c41d2","reason":"reason"}'

Example response

{
  "appointment_id": "ap_90311245",
  "status": "Cancelled",
  "reason": "Team member unwell",
  "scope": "this_appointment",
  "client_notified": true
}

Add client

write

POST https://cotlet.dev/api/v1/fresha/add_client

Creates a client profile and returns its client_id, which you pass to create_appointment. It sends nothing to the client and reaches no one, so it runs without asking first. Search with list_clients first to avoid a duplicate profile.

Parameters

Parameters: add_client
NameTypeDescription
first_name required string First name.
last_name string Last name.
email string Email.
phone string Mobile number with country code.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/add_client \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"first_name":"first name"}'
Python
result = client.call("fresha", "add_client", {"first_name":"first name"})
Node.js
const result = await cotlet.call('fresha', 'add_client', {"first_name":"first name"});
CLI
cotlet call fresha add_client --data '{"first_name":"first name"}'

Example response

{
  "client_id": "cl_48213990",
  "name": "Maya Patel",
  "phone": "+44 7700 900456"
}

POST https://cotlet.dev/api/v1/fresha/add_client_note

Adds a simple note to a client profile. Notes are private: only your team sees them, never the client. Fresha cannot recover a deleted note, so the agent adds notes and never deletes them. It reaches no one, so it runs without asking first.

Parameters

Parameters: add_client_note
NameTypeDescription
client_id required string Client ID from list_clients.
text required string Note text.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/add_client_note \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"client_id":"client_8c41d2","text":"text"}'
Python
result = client.call("fresha", "add_client_note", {"client_id":"client_8c41d2","text":"text"})
Node.js
const result = await cotlet.call('fresha', 'add_client_note', {"client_id":"client_8c41d2","text":"text"});
CLI
cotlet call fresha add_client_note --data '{"client_id":"client_8c41d2","text":"text"}'

Example response

{
  "client_id": "cl_48213977",
  "note_id": "nt_5521",
  "created_at": "2026-10-11T09:12:00+01:00"
}

POST https://cotlet.dev/api/v1/fresha/add_blocked_time

Blocks a time range in one team member's calendar, so no one can book it. Use title for a reason such as one of your blocked time types, and description for a note your team sees. It reaches no client and is removed in Fresha in one step, so it runs without asking first.

Parameters

Parameters: add_blocked_time
NameTypeDescription
team_member_id required string Team member from list_team_members.
start_at required datetime ISO 8601 with time zone.
end_at required datetime ISO 8601 with time zone.
title string Reason, e.g. one of your blocked time types.
description string Note your team sees on the block.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/fresha/add_blocked_time \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"team_member_id":"team_member_8c41d2","start_at":"2026-10-01T09:00:00Z","end_at":"2026-10-01T09:00:00Z"}'
Python
result = client.call("fresha", "add_blocked_time", {"team_member_id":"team_member_8c41d2","start_at":"2026-10-01T09:00:00Z","end_at":"2026-10-01T09:00:00Z"})
Node.js
const result = await cotlet.call('fresha', 'add_blocked_time', {"team_member_id":"team_member_8c41d2","start_at":"2026-10-01T09:00:00Z","end_at":"2026-10-01T09:00:00Z"});
CLI
cotlet call fresha add_blocked_time --data '{"team_member_id":"team_member_8c41d2","start_at":"2026-10-01T09:00:00Z","end_at":"2026-10-01T09:00:00Z"}'

Example response

{
  "blocked_time_id": "bt_3302",
  "team_member": "Sam",
  "start_at": "2026-10-14T13:00:00+01:00",
  "end_at": "2026-10-14T14:00:00+01:00",
  "title": "Training"
}