php bash javascript

Info

Welcome to the generated API reference. Get Postman Collection

This API is being developed constantly with more endpoints being added regularly.

Authentication

All endpoints require authentication via the Authorization: Bearer {access_token} header. There are 2 ways to get the Access Token:

  1. The user creates a token themselves directly in EasyPractice. This requires no set up upfront and is recommended for small/test environments where you will only need data access for one or two users.
  2. OAuth2 flow. This requires you to perform additional actions, such as redirecting a user from your app to EasyPractice and then exchanging the received authorization code for an Access Token. This is a more user-friendly approach and will work great for multi-user platforms.

Authentication - Scopes

Scopes constrains the endpoints to which a client has access. Once you create a Personal Access Token, you cannot redefine the scopes.

Note: Most of the scopes are not required until 1st of November 2021 in order to allow existing integrations time to migrate to a scope-based authentication. On the 1st of November 2021 the scope requirements will be enforced on every route that has scope(s) documented as a requirement.

Currently the following scopes are available:

Scope Description
clients-read Ability to view clients
clients-readwrite Ability to view and edit clients
journals-read Ability to view client journals
journals-readwrite Ability to view and edit client journals
journal-files-read Ability to view and download client journal files
calendars-read Ability to view calendars and appointments
calendars-readwrite Ability to view and edit calendars and appointments
invoices-read Ability to view invoices
invoices-readwrite Ability to view and edit invoices
easypay-read Ability to view information about online payments

#Authentication - Personal Access Tokens

  1. Log in to your EasyPractice account (or create a new account)
  2. Go to the Apps page and activate the API app.
  3. Go to the API Settings page
  4. In the Personal Access Tokens section click on the Create New Token button to generate a new token. Remember to choose the scopes you require.
  5. Copy the new generated token. You will NOT be able to view it again, so keep it safe. In case you lose it, you can generate as many new tokens as you'd like.
  6. Add an Authorization header to your requests with the value of Bearer {token} where {token} is your token you copied earlier.

#Authentication - OAuth2 We also support the standard OAuth2 flow for authenticating requests. Your app will be able to "Request access to EasyPractice", where the user will be able to either Authorize or Deny the request. This is a much more user-friendly approach, but requires more set up in advance.

Step 1 - Creating a OAuth2 Client

In order to authenticate with EasyPractice, you'll need to set up a new Client so we can identify who's accessing the API, as well as to help the user know what app is requiring their EasyPractice data.

  1. Log in to your EasyPractice account (or create a new account)
  2. Go to the Apps page and activate the API app.
  3. Go to the API Settings page
  4. In the OAuth Clients section click on the Create New Client button to set up a new Client. The "Name" will be visible to your users when they try to connect to EasyPractice. The "Redirect URL" is your publicly accessible website address, where we'll send the user back after they have accepted or denied the request to connect.
  5. Once created, note down the Client's ID and the Secret. You will need them later.

Step 2 - Initiating the authorization request

To begin the OAuth2 authorization flow, you'll need to redirect your user to a special EasyPractice page with a few query parameters.

Example: https://system.easypractice.net/oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=code&scope[]=journals-readwrite&scope[]=calendars-readwrite&state=STATE

HTTP Request

GET oauth/authorize

Query Parameters

Parameter Type Status Description
client_id integer required The OAuth Client's ID that you have set up earlier.
redirect_uri string required The OAuth Client's Redirect URL that you have set up earlier.
response_type string required Always "code".
scope string optional A string or array of supported scopes.
state string optional custom string (usually a hash) that you keep on your user's session. This is not required, but it will be returned back to you after authorization to help you identify the user session if needed.

Once you redirect the user to the above URL, they will be prompted that your app wants to connect to their EasyPractice account. The user will see the list of scopes requested, so they know what data will be shared with your app.

They can either Approve (Authorize) or Deny the request. Either way, they will be redirected back to the REDIRECT_URL that you have supplied, along with an Authorization Code that you will need to exchange for a valid Access Token.

Step 3 - Converting Authorization Code to Access Token

Example Access Token request:

{
    "grant_type": "authorization_code",
    "client_id": 5,
    "client_secret": "N7ecQ5dxS4wYXyEi2PCon0ldF5PfnoSmCxv1J0Or",
    "redirect_uri": "https://example.com/callback",
    "code": "def50200d2712ea33c5a36c273505502a785d5aa8fe0...",
    "scope": ["journals-readwrite", "calendars-readwrite"]
}

Once the user has been redirected back, you'll receive a few query parameters, one of which will be a code parameter.

HTTP Request

POST oauth/token

Body Parameters

Parameter Type Status Description
grant_type string required Always "authorization_code".
client_id integer required The OAuth Client's ID that you have set up earlier.
client_secret string required The OAuth Client's Secret that you have set up earlier.
redirect_uri string required The OAuth Client's Redirect URL that you have set up earlier.
code string required The code parameter value that you have received from the authorization request.

Example Access Token response:

{
    "token_type": "Bearer",
    "expires_in": 31536000,
    "access_token": "eyJ0eXAiOiJKV1QiLCJhb...",
    "refresh_token": "def502005caf0df4486d4e7..."
}

Response data values explained:

Step 4 (optional) - Refreshing Access Tokens

Access Tokens have an expiry date. If it's not refreshed, the user will have to re-authorize your app again (Step 2). In order to fetch a new Access Token by using a Refresh Token, you'll need to make a POST request to https://system.easypractice.net/oauth/token.

Example Refresh Token request:

{
    "grant_type": "refresh_token",
    "refresh_token": "def502005caf0df4486d4e7cbad6159ee5...",
    "client_id": 5,
    "client_secret": "N7ecQ5dxS4wYXyEi2PCon0ldF5PfnoSmCxv1J0Or",
    "scope": ["journals-readwrite", "calendars-readwrite"]
}

HTTP Request

POST oauth/token

Body Parameters

Parameter Type Status Description
grant_type string required Always "refresh_token" when refreshing an access token.
refresh_token string required The Refresh Token that you have received before in Step 3.
client_id integer required The OAuth Client's ID that you have set up earlier.
client_secret string required The OAuth Client's Secret that you have set up earlier.
scope string optional A string or array of supported scopes.

Example Refresh Token response:

{
    "token_type": "Bearer",
    "expires_in": 31536000,
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiI...",
    "refresh_token": "def50200734e746461d1d5a84420c4bab..."
}

Response data values explained:

Support

If you have any questions about setting up OAuth2 flow with our API, please get in touch with us at api@easypractice.net.

Bookings

List bookings


Requires authentication Returns all of user's bookings. Use the filter options provided to narrow down the results. For example: GET api/v1/bookings?start=2019-06-15&order_by_date=asc

Scope : bookings-read or bookings-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/bookings", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "start" => "2019-03-25",
            "end" => "2019-03-25",
            "client_id" => "54347",
            "calendar_id" => "54347",
            "product_id" => "54347",
            "employee_id" => "23",
            "status" => "absent",
            "order_by_date" => "asc",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/bookings" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/bookings");

    let params = {
            "start": "2019-03-25",
            "end": "2019-03-25",
            "client_id": "54347",
            "calendar_id": "54347",
            "product_id": "54347",
            "employee_id": "23",
            "status": "absent",
            "order_by_date": "asc",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "start": "2019-06-15T16:25:00+02:00",
            "start_formatted": "Saturday 15 June 2019, 16:25",
            "end": "2019-06-15T17:15:00+02:00",
            "end_formatted": "Saturday 15 June 2019, 17:15",
            "calendar_id": 167,
            "calendar_name": "Main calendar",
            "employee_id": 23,
            "employee_name": "John Doe",
            "type": "client_booking",
            "status": "finished",
            "heading": "",
            "notes": "First time for this client",
            "recurring": null,
            "until": null,
            "client_id": 1348,
            "client_name": "John Smith",
            "notify_email": 0,
            "notify_phone": 0,
            "created_via_api": true,
            "products": [
                {
                    "id": 125,
                    "name": "Main service"
                }
            ],
            "updated_at": "2022-06-15T17:15:00+02:00",
            "created_at": "2022-06-15T17:15:00+02:00"
        },
        {
            "id": 2,
            "start": "2019-09-30T17:55:00+02:00",
            "start_formatted": "Monday 30 September 2019, 17:55",
            "end": "2019-09-30T21:30:00+02:00",
            "end_formatted": "Monday 30 September 2019, 21:30",
            "calendar_id": 173,
            "calendar_name": "201",
            "employee_id": 23,
            "employee_name": "John Doe",
            "type": "Client appointment",
            "status": "finished",
            "heading": "",
            "notes": "First time for this client",
            "recurring": null,
            "until": null,
            "invoice_id": 1239,
            "invoice_status": "payed",
            "client_id": 648,
            "client_name": "Mary Williams",
            "notify_email": 1,
            "notify_phone": 0,
            "created_via_api": false,
            "products": [
                {
                    "id": 125,
                    "name": "Main service",
                    "addons": [
                        {
                            "id": 434,
                            "name": "Extra time"
                        }
                    ]
                },
                {
                    "id": 264,
                    "name": "Other service"
                }
            ],
            "updated_at": "2022-06-15T17:15:00+02:00",
            "created_at": "2022-06-15T17:15:00+02:00"
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/bookings

Query Parameters

Parameter Status Description
start optional Return only the bookings scheduled on or after the given date.
end optional Return only the bookings scheduled on or before the given date.
client_id optional Return only bookings belonging to this client ID.
calendar_id optional Return only bookings belonging to this calendar ID.
product_id optional Return only bookings belonging to this product ID.
employee_id optional Return only bookings belonging to this employee ID.
status optional Return only booking with the given status (must have "Appointment Status" app active).
order_by_date optional =[asc|desc] Order results by date.
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Create booking


Requires authentication Creates a new booking with the given data.

Scope : bookings-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->post("api/v1/bookings", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "start" => "2019-03-25T10:00:00+02:00",
        "end" => "2019-03-25T10:00:00+02:00",
        "calendar_id" => 54347,
        "employee_id" => 23,
        "type" => "client_booking",
        "status" => "not_started",
        "heading" => "New skill course",
        "notes" => "First time for this client",
        "recurring" => "daily",
        "until" => "2019-03-28",
        "client_id" => 1594,
        "send_receipt" => true,
        "send_phone_receipt" => false,
        "notify_email" => true,
        "notify_phone" => false,
        "products" => [
            [
                "id" => 1574,
            ],
            [
                "id" => 2164,
                "addons" => [
                    [
                        "id" => 434,
                    ],
                ],
            ],
        ],
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X POST "http://localhost/api/v1/bookings" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"start":"2019-03-25T10:00:00+02:00","end":"2019-03-25T10:00:00+02:00","calendar_id":54347,"employee_id":23,"type":"client_booking","status":"not_started","heading":"New skill course","notes":"First time for this client","recurring":"daily","until":"2019-03-28","client_id":1594,"send_receipt":1,"send_phone_receipt":false,"notify_email":1,"notify_phone":false,"products":[{"id":1574},{"id":2164,"addons":[{"id":434}]}]}'

const url = new URL("http://localhost/api/v1/bookings");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "start": "2019-03-25T10:00:00+02:00",
    "end": "2019-03-25T10:00:00+02:00",
    "calendar_id": 54347,
    "employee_id": 23,
    "type": "client_booking",
    "status": "not_started",
    "heading": "New skill course",
    "notes": "First time for this client",
    "recurring": "daily",
    "until": "2019-03-28",
    "client_id": 1594,
    "send_receipt": 1,
    "send_phone_receipt": false,
    "notify_email": 1,
    "notify_phone": false,
    "products": [
        {
            "id": 1574
        },
        {
            "id": 2164,
            "addons": [
                {
                    "id": 434
                }
            ]
        }
    ]
}

fetch(url, {
    method: "POST",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (201):

{
    "message": "Created",
    "data": {
        "id": 1,
        "start": "2019-06-15T16:25:00+02:00",
        "start_formatted": "Saturday 15 June 2019, 16:25",
        "end": "2019-06-15T17:15:00+02:00",
        "end_formatted": "Saturday 15 June 2019, 17:15",
        "calendar_id": 167,
        "calendar_name": "Main calendar",
        "employee_id": 23,
        "employee_name": "John Doe",
        "type": "client_booking",
        "status": "finished",
        "heading": "",
        "notes": "First time for this client",
        "recurring": null,
        "until": null,
        "invoice_id": null,
        "invoice_status": null,
        "client_id": 1348,
        "client_name": "John Smith",
        "notify_email": 0,
        "notify_phone": 0,
        "products": [
            {
                "id": 125,
                "name": "Main service",
                "addons": [
                    {
                        "id": 434,
                        "name": "Extra time"
                    }
                ]
            }
        ],
        "updated_at": "2022-06-15T17:15:00+02:00",
        "created_at": "2022-06-15T17:15:00+02:00"
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "end": [
            "The End time must be after the Start time."
        ]
    }
}

HTTP Request

POST api/v1/bookings

Body Parameters

Parameter Type Status Description
start date_format:Y-m-d\TH:i:sP required The start date for the appointment in the full ISO 8601 format (e.g. 2019-10-30T08:50:31+00:00).
end date_format:Y-m-d\TH:i:sP required The end date for the appointment in the full ISO 8601 format (e.g. 2019-10-30T08:50:31+00:00).
calendar_id integer required The calendar ID associated to the appointment.
employee_id integer optional The user employee ID associated to the appointment.
type string required The appointment type =['other_booking', 'close_online_booking', 'client_booking'].
status string optional The status of the appointment. Must have the "Appointment Status" app enabled.
heading string optional The appointment title (It's required only for 'other_booking' type).
notes string optional The appointment additional notes.
recurring string optional How often this appointment is repeated =[false, 'daily', 'weekly', 'every_other_week'] (It's required for 'other_booking' and 'close_online_booking' types).
until date_format:Y-m-d optional Until what date this appointment is repeated (It's required if recurring field is different than false).
client_id integer optional The client ID associated to the appointment (It's required for 'client_booking' type).
send_receipt boolean optional Should we send the confirmation email straight away? Client must have a valid email address.
send_phone_receipt boolean optional Should we send the confirmation SMS straight away? Client must have a valid phone number.
notify_email boolean optional Should we send the reminder email to the client the day before the appointment.
notify_phone boolean optional Should we send the reminder SMS to the client the day before the appointment.
products array optional The products associated to the appointment (It's required for 'client_booking' type).

Create multiple bookings


Requires authentication Create multiple (up to 100) bookings with a single request

The root-level "bookings" attribute must be used to provide an array of bookings to be created.

Each booking object in the array has the same rules as when creating a single booking via the "Create booking" endpoint. If there's at least one booking with a validation error, none will be created.

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->post("api/v1/bookings/bulk", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "bookings" => [
            [
                "start" => "2019-03-25T10:00:00+02:00",
                "end" => "2019-03-25T10:00:00+02:00",
                "calendar_id" => 54347,
                "employee_id" => 23,
                "type" => "client_booking",
                "status" => "not_started",
                "heading" => "New skill course",
                "notes" => "First time for this client",
                "recurring" => "daily",
                "until" => "2019-03-28",
                "client_id" => 1594,
                "send_receipt" => true,
                "send_phone_receipt" => false,
                "notify_email" => true,
                "notify_phone" => false,
                "products" => [
                    [
                        "id" => 1574,
                    ],
                    [
                        "id" => 2164,
                        "addons" => [
                            [
                                "id" => 434,
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X POST "http://localhost/api/v1/bookings/bulk" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"bookings":[{"start":"2019-03-25T10:00:00+02:00","end":"2019-03-25T10:00:00+02:00","calendar_id":54347,"employee_id":23,"type":"client_booking","status":"not_started","heading":"New skill course","notes":"First time for this client","recurring":"daily","until":"2019-03-28","client_id":1594,"send_receipt":1,"send_phone_receipt":false,"notify_email":1,"notify_phone":false,"products":[{"id":1574},{"id":2164,"addons":[{"id":434}]}]}]}'

const url = new URL("http://localhost/api/v1/bookings/bulk");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "bookings": [
        {
            "start": "2019-03-25T10:00:00+02:00",
            "end": "2019-03-25T10:00:00+02:00",
            "calendar_id": 54347,
            "employee_id": 23,
            "type": "client_booking",
            "status": "not_started",
            "heading": "New skill course",
            "notes": "First time for this client",
            "recurring": "daily",
            "until": "2019-03-28",
            "client_id": 1594,
            "send_receipt": 1,
            "send_phone_receipt": false,
            "notify_email": 1,
            "notify_phone": false,
            "products": [
                {
                    "id": 1574
                },
                {
                    "id": 2164,
                    "addons": [
                        {
                            "id": 434
                        }
                    ]
                }
            ]
        }
    ]
}

fetch(url, {
    method: "POST",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (201):

{
    "message": "Created",
    "data": [
        {
            "id": 1,
            "start": "2019-06-15T16:25:00+02:00",
            "start_formatted": "Saturday 15 June 2019, 16:25",
            "end": "2019-06-15T17:15:00+02:00",
            "end_formatted": "Saturday 15 June 2019, 17:15",
            "calendar_id": 167,
            "calendar_name": "Main calendar",
            "employee_id": 23,
            "employee_name": "John Doe",
            "type": "client_booking",
            "status": "finished",
            "heading": "",
            "notes": "First time for this client",
            "recurring": null,
            "until": null,
            "invoice_id": null,
            "invoice_status": null,
            "client_id": 1348,
            "client_name": "John Smith",
            "notify_email": 0,
            "notify_phone": 0,
            "products": [
                {
                    "id": 125,
                    "name": "Main service",
                    "addons": [
                        {
                            "id": 434,
                            "name": "Extra time"
                        }
                    ]
                }
            ],
            "updated_at": "2022-06-15T17:15:00+02:00",
            "created_at": "2022-06-15T17:15:00+02:00"
        }
    ]
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "bookings.0.end": [
            "The End time must be after the Start time."
        ]
    }
}

HTTP Request

POST api/v1/bookings/bulk

Body Parameters

Parameter Type Status Description
bookings.0.start date_format:Y-m-d\TH:i:sP required The start date for the appointment in the full ISO 8601 format (e.g. 2019-10-30T08:50:31+00:00).
bookings.0.end date_format:Y-m-d\TH:i:sP required The end date for the appointment in the full ISO 8601 format (e.g. 2019-10-30T08:50:31+00:00).
bookings.0.calendar_id integer required The calendar ID associated to the appointment.
bookings.0.employee_id integer optional The user employee ID associated to the appointment.
bookings.0.type string required The appointment type =['other_booking', 'close_online_booking', 'client_booking'].
bookings.0.status string optional The status of the appointment. Must have the "Appointment Status" app enabled.
bookings.0.heading string optional The appointment title (It's required only for 'other_booking' type).
bookings.0.notes string optional The appointment additional notes.
bookings.0.recurring string optional How often this appointment is repeated =[false, 'daily', 'weekly', 'every_other_week'] (It's required for 'other_booking' and 'close_online_booking' types).
bookings.0.until date_format:Y-m-d optional Until what date this appointment is repeated (It's required if recurring field is different than false).
bookings.0.client_id integer optional The client ID associated to the appointment (It's required for 'client_booking' type).
bookings.0.send_receipt boolean optional Should we send the confirmation email straight away? Client must have a valid email address.
bookings.0.send_phone_receipt boolean optional Should we send the confirmation SMS straight away? Client must have a valid phone number.
bookings.0.notify_email boolean optional Should we send the reminder email to the client the day before the appointment.
bookings.0.notify_phone boolean optional Should we send the reminder SMS to the client the day before the appointment.
bookings.0.products array optional The products associated to the appointment (It's required for 'client_booking' type).

Show booking


Requires authentication Returns the booking data for a given ID.

Scope : bookings-read or bookings-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/bookings/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/bookings/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/bookings/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "start": "2019-06-15T16:25:00+02:00",
    "start_formatted": "Saturday 15 June 2019, 16:25",
    "end": "2019-06-15T17:15:00+02:00",
    "end_formatted": "Saturday 15 June 2019, 17:15",
    "calendar_id": 167,
    "calendar_name": "Main calendar",
    "employee_id": 23,
    "employee_name": "John Doe",
    "type": "client_booking",
    "status": "finished",
    "heading": "",
    "notes": "First time for this client",
    "recurring": null,
    "until": null,
    "invoice_id": 1239,
    "invoice_status": "payed",
    "client_id": 1348,
    "client_name": "John Smith",
    "notify_email": 0,
    "notify_phone": 0,
    "created_via_api": false,
    "products": [
        {
            "id": 125,
            "name": "Main service",
            "addons": [
                {
                    "id": 434,
                    "name": "Extra time"
                }
            ]
        }
    ],
    "updated_at": "2022-06-15T17:15:00+02:00",
    "created_at": "2022-06-15T17:15:00+02:00"
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/bookings/{booking}

Update booking


Requires authentication Updates the booking.

Scope : bookings-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->put("api/v1/bookings/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "start" => "2019-03-25T10:00:00+02:00",
        "end" => "2019-03-25T10:00:00+02:00",
        "calendar_id" => 54347,
        "employee_id" => 23,
        "status" => "finished",
        "heading" => "New skill course",
        "notes" => "First time for this client",
        "recurring" => "daily",
        "until" => "2019-03-28",
        "client_id" => 1594,
        "notify_email" => true,
        "notify_phone" => false,
        "products" => [
            [
                "id" => 1574,
            ],
            [
                "id" => 2164,
                "addons" => [
                    [
                        "id" => 434,
                    ],
                ],
            ],
        ],
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X PUT "http://localhost/api/v1/bookings/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"start":"2019-03-25T10:00:00+02:00","end":"2019-03-25T10:00:00+02:00","calendar_id":54347,"employee_id":23,"status":"finished","heading":"New skill course","notes":"First time for this client","recurring":"daily","until":"2019-03-28","client_id":1594,"notify_email":1,"notify_phone":false,"products":[{"id":1574},{"id":2164,"addons":[{"id":434}]}]}'

const url = new URL("http://localhost/api/v1/bookings/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "start": "2019-03-25T10:00:00+02:00",
    "end": "2019-03-25T10:00:00+02:00",
    "calendar_id": 54347,
    "employee_id": 23,
    "status": "finished",
    "heading": "New skill course",
    "notes": "First time for this client",
    "recurring": "daily",
    "until": "2019-03-28",
    "client_id": 1594,
    "notify_email": 1,
    "notify_phone": false,
    "products": [
        {
            "id": 1574
        },
        {
            "id": 2164,
            "addons": [
                {
                    "id": 434
                }
            ]
        }
    ]
}

fetch(url, {
    method: "PUT",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Updated",
    "data": {
        "id": 1,
        "start": "2019-06-15T16:25:00+02:00",
        "start_formatted": "Saturday 15 June 2019, 16:25",
        "end": "2019-06-15T17:15:00+02:00",
        "end_formatted": "Saturday 15 June 2019, 17:15",
        "calendar_id": 167,
        "calendar_name": "Main calendar",
        "employee_id": 23,
        "employee_name": "John Doe",
        "type": "client_booking",
        "status": "finished",
        "heading": "",
        "notes": "First time for this client",
        "recurring": null,
        "until": null,
        "invoice_id": null,
        "invoice_status": null,
        "client_id": 1348,
        "client_name": "John Smith",
        "notify_email": 0,
        "notify_phone": 0,
        "products": [
            {
                "id": 125,
                "name": "Main service",
                "addons": [
                    {
                        "id": 434,
                        "name": "Extra time"
                    }
                ]
            }
        ],
        "updated_at": "2022-06-15T17:15:00+02:00",
        "created_at": "2022-06-15T17:15:00+02:00"
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "name": [
            "The Name field is required."
        ]
    }
}

HTTP Request

PUT api/v1/bookings/{booking}

PATCH api/v1/bookings/{booking}

Body Parameters

Parameter Type Status Description
start date_format:Y-m-d\TH:i:sP optional The start date for the appointment in the full ISO 8601 format (e.g. 2019-10-30T08:50:31+00:00).
end date_format:Y-m-d\TH:i:sP optional The end date for the appointment in the full ISO 8601 format (e.g. 2019-10-30T08:50:31+00:00).
calendar_id integer optional The calendar ID associated to the appointment.
employee_id integer optional The user employee ID associated to the appointment.
status string optional The status of the appointment. Must have the "Appointment Status" app enabled.
heading string optional The appointment heading for 'other_booking' type.
notes string optional The appointment additional notes.
recurring string optional How often this appointment is repeated =[false, 'daily', 'weekly', 'every_other_week'] (It's required for 'other_booking' and 'close_online_booking' types).
until date_format:Y-m-d optional Until what date this appointment is repeated (It's required if recurring field is different than false).
client_id integer optional The client ID associated to the appointment.
notify_email boolean optional Should we send the reminder email to the client the day before the appointment.
notify_phone boolean optional Should we send the reminder SMS to the client the day before the appointment.
products array optional The products associated to the appointment.

Delete booking


Requires authentication Deletes the booking.

Scope : bookings-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->delete("api/v1/bookings/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X DELETE "http://localhost/api/v1/bookings/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/bookings/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "DELETE",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Deleted"
}

HTTP Request

DELETE api/v1/bookings/{booking}

Calendar Opening Times

Show Opening Times


Requires authentication Returns opening times for a Calendar. For example: GET api/v1/calendars/1/opening-times

Scope: calendars-read or calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/calendars/1/opening-times", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/calendars/1/opening-times" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/calendars/1/opening-times");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": {
        "id": 1,
        "user_id": 1,
        "calendar_id": 1,
        "show_in_calendar": true,
        "times": {
            "monday_start": "09:00",
            "monday_end": "17:00",
            "monday_closed": false,
            "tuesday_start": "09:00",
            "tuesday_end": "17:00",
            "tuesday_closed": false,
            "wednesday_start": "09:00",
            "wednesday_end": "17:00",
            "wednesday_closed": false,
            "thursday_start": "09:00",
            "thursday_end": "17:00",
            "thursday_closed": false,
            "friday_start": "09:00",
            "friday_end": "17:00",
            "friday_closed": false,
            "saturday_start": "09:00",
            "saturday_end": "17:00",
            "saturday_closed": true,
            "sunday_start": "09:00",
            "sunday_end": "17:00",
            "sunday_closed": true
        }
    }
}

HTTP Request

GET api/v1/calendars/{calendar}/opening-times

Update Opening Times


Requires authentication Updates an Opening Times entry for a given calendar. For example: PUT api/v1/calendars/1/opening-times

Scope: calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->put("api/v1/calendars/1/opening-times", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "show_in_calendar" => true,
        "times" => [
            "monday_start" => "09:00",
            "monday_end" => "17:00",
            "monday_closed" => false,
            "tuesday_start" => "09:00",
            "tuesday_end" => "17:00",
            "tuesday_closed" => false,
            "wednesday_start" => "09:00",
            "wednesday_end" => "17:00",
            "wednesday_closed" => false,
            "thursday_start" => "09:00",
            "thursday_end" => "17:00",
            "thursday_closed" => false,
            "friday_start" => "09:00",
            "friday_end" => "17:00",
            "friday_closed" => false,
            "saturday_start" => "09:00",
            "saturday_end" => "17:00",
            "saturday_closed" => false,
            "sunday_start" => "09:00",
            "sunday_end" => "17:00",
            "sunday_closed" => false,
        ],
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X PUT "http://localhost/api/v1/calendars/1/opening-times" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"show_in_calendar":1,"times":{"monday_start":"09:00","monday_end":"17:00","monday_closed":false,"tuesday_start":"09:00","tuesday_end":"17:00","tuesday_closed":false,"wednesday_start":"09:00","wednesday_end":"17:00","wednesday_closed":false,"thursday_start":"09:00","thursday_end":"17:00","thursday_closed":false,"friday_start":"09:00","friday_end":"17:00","friday_closed":false,"saturday_start":"09:00","saturday_end":"17:00","saturday_closed":false,"sunday_start":"09:00","sunday_end":"17:00","sunday_closed":false}}'

const url = new URL("http://localhost/api/v1/calendars/1/opening-times");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "show_in_calendar": 1,
    "times": {
        "monday_start": "09:00",
        "monday_end": "17:00",
        "monday_closed": false,
        "tuesday_start": "09:00",
        "tuesday_end": "17:00",
        "tuesday_closed": false,
        "wednesday_start": "09:00",
        "wednesday_end": "17:00",
        "wednesday_closed": false,
        "thursday_start": "09:00",
        "thursday_end": "17:00",
        "thursday_closed": false,
        "friday_start": "09:00",
        "friday_end": "17:00",
        "friday_closed": false,
        "saturday_start": "09:00",
        "saturday_end": "17:00",
        "saturday_closed": false,
        "sunday_start": "09:00",
        "sunday_end": "17:00",
        "sunday_closed": false
    }
}

fetch(url, {
    method: "PUT",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Your opening hours were updated",
    "data": {
        "id": 1,
        "user_id": 1,
        "calendar_id": 1,
        "show_in_calendar": true,
        "times": {
            "monday_start": "09:00",
            "monday_end": "17:00",
            "monday_closed": false,
            "tuesday_start": "09:00",
            "tuesday_end": "17:00",
            "tuesday_closed": false,
            "wednesday_start": "09:00",
            "wednesday_end": "17:00",
            "wednesday_closed": false,
            "thursday_start": "09:00",
            "thursday_end": "17:00",
            "thursday_closed": false,
            "friday_start": "09:00",
            "friday_end": "17:00",
            "friday_closed": false,
            "saturday_start": "09:00",
            "saturday_end": "17:00",
            "saturday_closed": true,
            "sunday_start": "09:00",
            "sunday_end": "17:00",
            "sunday_closed": true
        }
    }
}

HTTP Request

PUT api/v1/calendars/{calendar}/opening-times

Body Parameters

Parameter Type Status Description
show_in_calendar boolean required The flag for whether this opening time is displayed on its calendar.
times.monday_start date_format:H:i required Monday Start time.
times.monday_end date_format:H:i required Monday End time.
times.monday_closed boolean required Monday Closed.
times.tuesday_start date_format:H:i required Tuesday Start time.
times.tuesday_end date_format:H:i required Tuesday End time.
times.tuesday_closed boolean required Tuesday Closed.
times.wednesday_start date_format:H:i required Wednesday Start time.
times.wednesday_end date_format:H:i required Wednesday End time.
times.wednesday_closed boolean required Wednesday Closed.
times.thursday_start date_format:H:i required Thursday Start time.
times.thursday_end date_format:H:i required Thursday End time.
times.thursday_closed boolean required Thursday Closed.
times.friday_start date_format:H:i required Friday Start time.
times.friday_end date_format:H:i required Friday End time.
times.friday_closed boolean required Friday Closed.
times.saturday_start date_format:H:i required Saturday Start time.
times.saturday_end date_format:H:i required Saturday End time.
times.saturday_closed boolean required Saturday Closed.
times.sunday_start date_format:H:i required Sunday Start time.
times.sunday_end date_format:H:i required Sunday End time.
times.sunday_closed boolean required Sunday Closed.

Calendar Pauses

List Pauses


Requires authentication Returns all of user's Pauses per each Calendar. For example: GET api/v1/calendars/1/pauses

Scope: calendars-read or calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/calendars/1/pauses", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/calendars/1/pauses" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/calendars/1/pauses");

    let params = {
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "calendar_id": 1,
            "weekday": null,
            "date": "2020-07-03",
            "start_time": "08:00",
            "end_time": "20:00"
        },
        {
            "id": 2,
            "calendar_id": 1,
            "weekday": 3,
            "date": null,
            "start_time": "12:00",
            "end_time": "12:30"
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/calendars/{calendar}/pauses

Query Parameters

Parameter Status Description
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Create a Pause


Requires authentication Create a new Pause entry. For example: POST api/v1/calendars/1/pauses

Scope: calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->post("api/v1/calendars/1/pauses", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "weekday" => 1,
        "date" => "2020-07-03",
        "start_time" => "08:00",
        "end_time" => "22:00",
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X POST "http://localhost/api/v1/calendars/1/pauses" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"weekday":1,"date":"2020-07-03","start_time":"08:00","end_time":"22:00"}'

const url = new URL("http://localhost/api/v1/calendars/1/pauses");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "weekday": 1,
    "date": "2020-07-03",
    "start_time": "08:00",
    "end_time": "22:00"
}

fetch(url, {
    method: "POST",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Pause Saved",
    "data": {
        "id": 1,
        "calendar_id": 1,
        "weekday": null,
        "date": "2020-07-03",
        "starttime": "08:00",
        "endtime": "22:00"
    }
}

HTTP Request

POST api/v1/calendars/{calendar}/pauses

Body Parameters

Parameter Type Status Description
weekday integer optional The number of the day of the week (eg: 1 - Monday, 7 - Sunday)
date date_format:Y-m-d optional The start date of a week long Pause.
start_time date_format:H:i required The start time of the Pause
end_time date_format:H:i required Th end time of the Pause

Show Pause


Requires authentication Returns a Pause. For example: GET api/v1/calendars/1/pauses/1

Scope: calendars-read or calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/calendars/1/pauses/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/calendars/1/pauses/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/calendars/1/pauses/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "calendar_id": 1,
    "weekday": null,
    "date": "2020-07-03",
    "start_time": "08:00",
    "end_time": "20:00"
}

HTTP Request

GET api/v1/calendars/{calendar}/pauses/{pause}

Update Pause


Requires authentication Updates a Pause entry for a given calendar. For example: PUT api/v1/calendars/1/pauses/1

Scope: calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->put("api/v1/calendars/1/pauses/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "weekday" => 1,
        "date" => "2020-07-03",
        "start_time" => "08:00",
        "end_time" => "22:00",
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X PUT "http://localhost/api/v1/calendars/1/pauses/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"weekday":1,"date":"2020-07-03","start_time":"08:00","end_time":"22:00"}'

const url = new URL("http://localhost/api/v1/calendars/1/pauses/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "weekday": 1,
    "date": "2020-07-03",
    "start_time": "08:00",
    "end_time": "22:00"
}

fetch(url, {
    method: "PUT",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Pause Saved",
    "data": {
        "id": 1,
        "calendar_id": 1,
        "weekday": null,
        "date": "2020-07-03",
        "starttime": "08:00",
        "endtime": "22:00"
    }
}

HTTP Request

PUT api/v1/calendars/{calendar}/pauses/{pause}

PATCH api/v1/calendars/{calendar}/pauses/{pause}

Body Parameters

Parameter Type Status Description
weekday integer optional The number of the day of the week (eg: 1 - Monday, 7 - Sunday)
date date_format:Y-m-d optional The start date of a week long Pause.
start_time date_format:H:i required The start time of the Pause
end_time date_format:H:i required Th end time of the Pause

Delete Pause


Requires authentication Deletes the Pause. For example: DELETE api/v1/calendars/1/pauses/1

Scope: calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->delete("api/v1/calendars/1/pauses/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X DELETE "http://localhost/api/v1/calendars/1/pauses/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/calendars/1/pauses/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "DELETE",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Deleted"
}

HTTP Request

DELETE api/v1/calendars/{calendar}/pauses/{pause}

Calendars

List calendars


Requires authentication Returns all of user's calendars. Use the filter options provided to narrow down the results. For example: GET api/v1/calendars?order_by_name=asc

Scope : calendars-read or calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/calendars", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "order_by_name" => "asc",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/calendars" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/calendars");

    let params = {
            "order_by_name": "asc",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "name": "Calendar",
            "description": "This is the description of a calendar",
            "color": "#C3E1FF",
            "type": "standard",
            "is_available_in_online_booking": true
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/calendars

Query Parameters

Parameter Status Description
order_by_name optional =[asc|desc] Order results by name.
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Create a calendar


Requires authentication Creates a new calendar with the given data.

Scope : calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->post("api/v1/calendars", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "name" => "Calendar name",
        "description" => "Calendar description",
        "color" => "#c3e1ff",
        "type" => "standard",
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X POST "http://localhost/api/v1/calendars" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"name":"Calendar name","description":"Calendar description","color":"#c3e1ff","type":"standard"}'

const url = new URL("http://localhost/api/v1/calendars");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "name": "Calendar name",
    "description": "Calendar description",
    "color": "#c3e1ff",
    "type": "standard"
}

fetch(url, {
    method: "POST",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (201):

{
    "message": "The calendar was added",
    "data": {
        "id": 1,
        "name": "Name of calendar",
        "description": "Description of calendar",
        "color": "#c3e1ff",
        "type": "standard",
        "is_available_in_online_booking": true
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "name": [
            "The Name field is required."
        ],
        "type": [
            "The type field is required.",
            "The type field needs to be 'standard'."
        ],
        "color": [
            "The color field is required.",
            "The color field is not a valid HEX value."
        ]
    }
}

HTTP Request

POST api/v1/calendars

Body Parameters

Parameter Type Status Description
name string required The name of the calendar
description string optional The description of the calendar
color string required The color (hex-value) of the calendar
type string required The calendar type (currently only "standard" is supported)

Show a calendar


Requires authentication Returns the calendar data for a given ID.

Scope : calendars-read or calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/calendars/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/calendars/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/calendars/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "name": "Calendar",
    "description": "This is the description of a calendar",
    "color": "#C3E1FF",
    "type": "standard",
    "is_available_in_online_booking": true
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/calendars/{calendar}

Client Custom Attributes

List attributes


Requires authentication Returns all of the possible custom client attributes which can be used when creating/updating cliengs. These custom attributes are created using the "Custom Fields" app in the system.

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/client-custom-attributes", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/client-custom-attributes" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/client-custom-attributes");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

[
    {
        "identifier": "birthday",
        "name": "Birthday",
        "type": "date",
        "required": false
    },
    {
        "identifier": "age",
        "name": "Age",
        "type": "number",
        "required": false
    }
]

HTTP Request

GET api/v1/client-custom-attributes

Show attribute


Requires authentication Returns the information about a custom attribute given its identifier.

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/client-custom-attributes/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/client-custom-attributes/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/client-custom-attributes/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "identifier": "birthday",
    "name": "Birthday",
    "type": "date",
    "required": false
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/client-custom-attributes/{identifier}

Client Journal Files

List client journal files


Requires authentication Returns information about all of the client's journal files.

Scope : journal-files-read

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/clients/1/journal-files", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/clients/1/journal-files" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/clients/1/journal-files");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

[
    {
        "id": 1,
        "file_name": "1537433053_253518_1537529505_photo.jpeg",
        "system_download_url": "https:\/\/system.easypractice.net\/app\/client\/1\/journal_file\/1",
        "api_download_url": "https:\/\/system.easypractice.net\/api\/v1\/clients\/1\/journal-files\/1\/download",
        "file_type": "jpeg",
        "created_at": "2021-08-19T23:39:04+02:00",
        "mime_type": "image\/jpeg",
        "size": 53776
    },
    {
        "id": 2,
        "file_name": "1537433053_253518_1537529505_document.pdf",
        "system_download_url": "https:\/\/system.easypractice.net\/app\/client\/1\/journal_file\/2",
        "api_download_url": "https:\/\/system.easypractice.net\/api\/v1\/clients\/1\/journal-files\/2\/download",
        "file_type": "pdf",
        "created_at": "2021-08-19T23:39:04+02:00",
        "mime_type": "application\/pdf",
        "size": 1187182
    }
]

HTTP Request

GET api/v1/clients/{client}/journal-files

Show client journal file


Requires authentication Returns the client journal file data for a given ID.

Scope : journal-files-read

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/clients/1/journal-files/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/clients/1/journal-files/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/clients/1/journal-files/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "file_name": "1537433053_253518_1537529505_photo.jpeg",
    "system_download_url": "https:\/\/system.easypractice.net\/app\/client\/1\/journal_file\/1",
    "api_download_url": "https:\/\/system.easypractice.net\/api\/v1\/clients\/1\/journal-files\/1\/download",
    "file_type": "jpeg",
    "created_at": "2021-08-19T23:39:04+02:00",
    "mime_type": "image\/jpeg",
    "size": 53776
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/clients/{client}/journal-files/{journal_file}

Download client journal file


Requires authentication Downloads the client journal file data for a given ID.

Scope : journal-files-read

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/clients/1/journal-files/1/download", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/clients/1/journal-files/1/download" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/clients/1/journal-files/1/download");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

Example response (401):

{
    "error": {
        "message": "Unauthenticated",
        "status_code": 401
    }
}

HTTP Request

GET api/v1/clients/{client}/journal-files/{journal_file}/download

Client Journals

List client journals


Requires authentication Returns all of specified clients journal entries. Use the filter options provided to narrow down the results. For example: GET api/v1/clients/{client_id}/journals?draft=1&order_by_date=desc

Scope : journals-read or journals-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/clients/1/journals", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "locked" => "1",
            "favorite" => "1",
            "draft" => "1",
            "order_by_date" => "desc",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/clients/1/journals" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/clients/1/journals");

    let params = {
            "locked": "1",
            "favorite": "1",
            "draft": "1",
            "order_by_date": "desc",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "date": "2020-01-23",
            "title": "Client Journal Title",
            "content": "Client Journal Content",
            "favorite": true,
            "draft": false,
            "locked": false,
            "locked_at": null
        },
        {
            "id": 3,
            "date": "2020-01-22",
            "title": "Client Journal Title example2",
            "content": "Client Journal Content example2",
            "favorite": false,
            "draft": true,
            "locked": true,
            "locked_at": "2020-01-26T18:10:47+00:00"
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/clients/{client}/journals

Query Parameters

Parameter Status Description
locked optional Return only locked journals.
favorite optional Return only favorited journals.
draft optional Return only draft journals. Drafts are entries auto-saved whilst typing.
order_by_date optional =[asc|desc] Order results by journal date.

Create client journal


Requires authentication Creates a new client journal with the given data.

Scope : journals-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->post("api/v1/clients/1/journals", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "date" => "2019-03-25",
        "content" => "The journal content",
        "favorite" => true,
        "draft" => false,
        "locked" => false,
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X POST "http://localhost/api/v1/clients/1/journals" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"date":"2019-03-25","content":"The journal content","favorite":1,"draft":false,"locked":false}'

const url = new URL("http://localhost/api/v1/clients/1/journals");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "date": "2019-03-25",
    "content": "The journal content",
    "favorite": 1,
    "draft": false,
    "locked": false
}

fetch(url, {
    method: "POST",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (201):

{
    "message": "The journal entry was saved",
    "data": {
        "id": 1,
        "date": "2019-03-25",
        "title": "The journal title",
        "content": "The journal content",
        "favorite": true,
        "draft": false,
        "locked": false,
        "locked_at": null
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "content": [
            "The Content field is required."
        ]
    }
}

HTTP Request

POST api/v1/clients/{client}/journals

Body Parameters

Parameter Type Status Description
date date_format:Y-m-d required The date for the journal in the YYYY-MM-DD format.
content string required The content for the journal.
favorite boolean optional Mark journal as favorite. Only one favorite per client is allowed. Marking this as a favorite will unmark other favorites from the same client.
draft boolean optional Mark journal as draft.
locked boolean optional Mark journal as locked. Locked journals can no longer be updated or deleted.

Show client journal


Requires authentication Returns the client journal data for a given ID.

Scope : journals-read or journals-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/clients/1/journals/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/clients/1/journals/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/clients/1/journals/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "date": "2020-01-23",
    "title": "Client Journal Title",
    "content": "Client Journal Content",
    "favorite": true,
    "draft": false,
    "locked": false,
    "locked_at": null
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/clients/{client}/journals/{journal}

Update client journal


Requires authentication Updates the client journal. Provide all properties. Any missing properties will be updated to null

Scope : journals-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->put("api/v1/clients/1/journals/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "date" => "2019-03-25",
        "content" => "The journal content",
        "favorite" => true,
        "draft" => false,
        "locked" => false,
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X PUT "http://localhost/api/v1/clients/1/journals/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"date":"2019-03-25","content":"The journal content","favorite":1,"draft":false,"locked":false}'

const url = new URL("http://localhost/api/v1/clients/1/journals/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "date": "2019-03-25",
    "content": "The journal content",
    "favorite": 1,
    "draft": false,
    "locked": false
}

fetch(url, {
    method: "PUT",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "The journal entry was saved",
    "data": {
        "id": 1,
        "date": "2019-03-25",
        "title": "The journal title",
        "content": "The journal content",
        "favorite": true,
        "draft": false,
        "locked": false,
        "locked_at": null
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "content": [
            "The Content field is required."
        ]
    }
}

HTTP Request

PUT api/v1/clients/{client}/journals/{journal}

PATCH api/v1/clients/{client}/journals/{journal}

Body Parameters

Parameter Type Status Description
date date_format:Y-m-d required The date for the journal in the YYYY-MM-DD format.
content string required The content for the journal.
favorite boolean optional Mark journal as favorite. Only one favorite per client is allowed. Marking this as a favorite will unmark other favorites from the same client.
draft boolean optional Mark journal as draft.
locked boolean optional Mark journal as locked. Locked journals can no longer be updated or deleted.

Delete client journal


Requires authentication Deletes the client journal.

Scope : journals-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->delete("api/v1/clients/1/journals/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X DELETE "http://localhost/api/v1/clients/1/journals/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/clients/1/journals/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "DELETE",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Deleted"
}

HTTP Request

DELETE api/v1/clients/{client}/journals/{journal}

Client Vouchers

List clients vouchers


Requires authentication Returns all of the client vouchers. Use the filter options provided to narrow down the results. For example: GET api/v1/client-vouchers?active=1&vat=1&forEvents=1&page_size=20

Scope: clients-read or clients-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/client-vouchers", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "active" => "1",
            "vat" => "1",
            "forEvents" => "1",
            "forServices" => "1",
            "client" => "1",
            "order_by_created_at" => "asc",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/client-vouchers" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/client-vouchers");

    let params = {
            "active": "1",
            "vat": "1",
            "forEvents": "1",
            "forServices": "1",
            "client": "1",
            "order_by_created_at": "asc",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "name": "Silver voucher",
            "is_for_events": false,
            "client_id": 1,
            "clips": 5,
            "clips_used": 2,
            "currency": "EUR",
            "vat": true,
            "vat_rate": 23,
            "price": "3.00",
            "expires_on": "2022-01-10",
            "created_at": "2019-09-29T17:55:00+02:00",
            "updated_at": "2019-09-30T17:55:00+02:00",
            "service": {
                "id": 1,
                "name": "Example of a service"
            }
        },
        {
            "id": 2,
            "name": "Gold voucher",
            "is_for_events": true,
            "client_id": 1,
            "clips": 10,
            "clips_used": 1,
            "currency": "EUR",
            "vat": true,
            "vat_rate": 23,
            "price": "25.00",
            "expires_on": null,
            "created_at": "2019-09-29T17:55:00+02:00",
            "updated_at": "2019-09-30T17:55:00+02:00",
            "service": null
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/client-vouchers

Query Parameters

Parameter Status Description
active optional Return only the active vouchers.
vat optional Return only the vouchers with vat.
forEvents optional Return only the vouchers used for events.
forServices optional Return only the vouchers used for services.
client optional Return only client vouchers belonging to the client id given.
order_by_created_at optional =[asc|desc] Order results by created at.
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Show client voucher


Requires authentication Returns the client voucher data for a given ID.

Scope: clients-read or clients-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/client-vouchers/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/client-vouchers/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/client-vouchers/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "name": "Silver voucher",
    "is_for_events": false,
    "client_id": 1,
    "clips": 5,
    "clips_used": 2,
    "currency": "EUR",
    "vat": true,
    "vat_rate": 23,
    "price": "3.00",
    "expires_on": "2022-01-10",
    "created_at": "2019-09-29T17:55:00+02:00",
    "updated_at": "2019-09-30T17:55:00+02:00",
    "service": {
        "id": 1,
        "name": "Example of a service"
    }
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/client-vouchers/{client_voucher}

Clients

List clients


Requires authentication Returns all of user's clients. Use the filter options provided to narrow down the results. For example: GET api/v1/clients?hasEmail&email=asc

Scope : clients-read or clients-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/clients", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "active" => "1",
            "inactive" => "1",
            "has_email" => "1",
            "email" => "john@email.com",
            "has_phone" => "1",
            "cpr" => "1205001234",
            "tag_id" => "54347",
            "order_by_name" => "asc",
            "order_by_created" => "desc",
            "order_by_email" => "asc",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/clients" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/clients");

    let params = {
            "active": "1",
            "inactive": "1",
            "has_email": "1",
            "email": "john@email.com",
            "has_phone": "1",
            "cpr": "1205001234",
            "tag_id": "54347",
            "order_by_name": "asc",
            "order_by_created": "desc",
            "order_by_email": "asc",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "name": "John Smith",
            "email": "john@mydomain.dk",
            "phone": "+45 61175736",
            "address": "159 Bailey Brooks",
            "address_line_2": "Kastanievej 17",
            "zip": "W10 6DY",
            "city": "Copenhagen",
            "zone": "Region Sjælland",
            "country": "da",
            "alpha2": "DK",
            "cpr": "1598431234",
            "date_of_birth": "1991-01-20",
            "notes": "Notes about John",
            "profile_image_url": "https:\/\/www.gravatar.com\/avatar\/5ea799d8476cbe0b1965c39e7c7dbd99?d=mm&s=200",
            "status": "active",
            "created_at": "2019-04-16 11:54:42",
            "tags": [],
            "custom_attributes": [
                {
                    "identifier": "birthday",
                    "value": "1991-05-23"
                }
            ],
            "danmark_group": null
        },
        {
            "id": 2,
            "name": "Mary Williams",
            "email": "m.williams@mycompany.dk",
            "phone": "+45 43612915",
            "address": "3 Mike Common",
            "address_line_2": "Kastanievej 17",
            "zip": "NG34 9HJ",
            "city": "Copenhagen",
            "zone": "Region Sjælland",
            "country": "da",
            "alpha2": "DK",
            "cpr": "3574261234",
            "date_of_birth": null,
            "notes": "",
            "profile_image_url": "https:\/\/www.gravatar.com\/avatar\/5ea799d8476cbe0b1965c39e7c7dbd99?d=mm&s=200",
            "status": "inactive",
            "created_at": "2019-04-20 08:14:21",
            "tags": [],
            "custom_attributes": [],
            "danmark_group": 1
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/clients

Query Parameters

Parameter Status Description
active optional Return only the active clients.
inactive optional Return only the inactive clients.
has_email optional Return only the clients with an email.
email optional Return only clients with a given email.
has_phone optional Return only the clients with phone number.
cpr optional Return only the clients with a given CPR number.
tag_id optional Return only clients belonging to this tag ID.
order_by_name optional =[asc|desc] Order results by name.
order_by_created optional =[asc|desc] Order results by creation date.
order_by_email optional =[asc|desc] Order results by email.
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Create client


Requires authentication Creates a new client with the given data.

Scope : clients-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->post("api/v1/clients", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "name" => "John Doe",
        "email" => "john.doe@example.com",
        "ean" => 5789012345678,
        "phone" => "+44 7380111233",
        "address" => "159 Bailey Brooks",
        "address_line_2" => "123 Main Street",
        "zip" => "M14 5PT",
        "city" => "Manchester",
        "zone" => "Northamptonshire",
        "country" => "en",
        "cpr" => "0123456789",
        "date_of_birth" => "1991-01-20",
        "notes" => "Some notes",
        "tags" => [
            [
                "id" => 15,
            ],
            [
                "name" => "Gold clients",
            ],
        ],
        "status" => "active",
        "custom_attributes" => [
            [
                "identifier" => "birthday",
                "value" => "1991-05-23",
            ],
        ],
        "danmark_group" => 1,
        "sync_to_mailchimp" => false,
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X POST "http://localhost/api/v1/clients" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"name":"John Doe","email":"john.doe@example.com","ean":5789012345678,"phone":"+44 7380111233","address":"159 Bailey Brooks","address_line_2":"123 Main Street","zip":"M14 5PT","city":"Manchester","zone":"Northamptonshire","country":"en","cpr":"0123456789","date_of_birth":"1991-01-20","notes":"Some notes","tags":[{"id":15},{"name":"Gold clients"}],"status":"active","custom_attributes":[{"identifier":"birthday","value":"1991-05-23"}],"danmark_group":1,"sync_to_mailchimp":false}'

const url = new URL("http://localhost/api/v1/clients");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "name": "John Doe",
    "email": "john.doe@example.com",
    "ean": 5789012345678,
    "phone": "+44 7380111233",
    "address": "159 Bailey Brooks",
    "address_line_2": "123 Main Street",
    "zip": "M14 5PT",
    "city": "Manchester",
    "zone": "Northamptonshire",
    "country": "en",
    "cpr": "0123456789",
    "date_of_birth": "1991-01-20",
    "notes": "Some notes",
    "tags": [
        {
            "id": 15
        },
        {
            "name": "Gold clients"
        }
    ],
    "status": "active",
    "custom_attributes": [
        {
            "identifier": "birthday",
            "value": "1991-05-23"
        }
    ],
    "danmark_group": 1,
    "sync_to_mailchimp": false
}

fetch(url, {
    method: "POST",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (201):

{
    "message": "Created",
    "data": {
        "id": 123,
        "name": "John Doe",
        "email": "john.doe@example.com",
        "ean": "5789012345678",
        "phone": "+44 7380111233",
        "address": "159 Bailey Brooks",
        "address_line_2": "123 Main Street",
        "zip": "M14 5PT",
        "city": "Manchester",
        "zone": "Northamptonshire",
        "country": "en",
        "alpha2": "GB",
        "cpr": "0123456789",
        "date_of_birth": "1991-01-20",
        "notes": "Some notes",
        "status": "active",
        "tags": [],
        "custom_attributes": [
            {
                "identifier": "birthday",
                "value": "1991-05-23"
            }
        ],
        "danmark_group": 1
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "name": [
            "The Name field is required."
        ]
    }
}

HTTP Request

POST api/v1/clients

Body Parameters

Parameter Type Status Description
name string required The Name of the client.
email string optional The Email of the client.
ean string optional The EAN-number of the client.
phone string optional The Phone of the client with the country code. If the country code is omitted, the account's default country code is applied.
address string optional The Address of the client.
address_line_2 string optional The Address Line 2 of the client.
zip string optional The Zip/Postcode of the client.
city string optional The City of the client.
zone string optional The State/Province/County of the client.
country string optional The Country code of the client.
cpr string optional The CPR of the client.
date_of_birth The optional date of birth of the client in YYYY-MM-DD format.
notes string optional The Notes of the client.
tags array optional The tags associated to the client. It's possible to use the id or the name for each tag.
status string optional The status of the client. Currently only two statuses are allowed: "active" and "inactive".
custom_attributes array optional The custom fields and their values for the client. Must have "Custom Fields" app enabled. Non-existent fields will be ignored. See Client Custom Attributes for a list of available attributes on your account.
danmark_group integer optional (Danish accounts only) The Sygeforsikring "danmark" group number for this client. Must be one of these values: null (not set), 0 (Ved ikke), 1 (Gruppe 1), 2 (Gruppe 2), 5 (Gruppe 5) or 6 (Gruppe 6). A valid CPR is required to assign a client to a "danmark" group.
sync_to_mailchimp boolean optional (Requires Mailchimp app active) Whether to sync this client to Mailchimp.

Show client


Requires authentication Returns the client data for a given ID.

Scope : clients-read or clients-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/clients/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/clients/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/clients/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "name": "John Smith",
    "email": "john@mydomain.dk",
    "phone": "+45 61175736",
    "address": "159 Bailey Brooks",
    "address_line_2": "123 Main Street",
    "zip": "W10 6DY",
    "city": "Copenhagen",
    "zone": "Region Sjælland",
    "country": "da",
    "alpha2": "DK",
    "cpr": "1598431234",
    "date_of_birth": "1991-01-20",
    "notes": "Notes about John",
    "profile_image_url": "https:\/\/www.gravatar.com\/avatar\/5ea799d8476cbe0b1965c39e7c7dbd99?d=mm&s=200",
    "status": "active",
    "created_at": "2019-04-16 11:54:42",
    "tags": [],
    "custom_attributes": [
        {
            "identifier": "birthday",
            "value": "1991-05-23"
        }
    ],
    "danmark_group": 1
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/clients/{client}

Update client


Requires authentication Updates the client.

Scope : clients-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->put("api/v1/clients/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "name" => "John Doe",
        "email" => "john.doe@example.com",
        "ean" => 5789012345678,
        "phone" => "+44 7380111233",
        "address" => "159 Bailey Brooks",
        "address_line_2" => "123 Main Street",
        "zip" => "M14 5PT",
        "city" => "Manchester",
        "zone" => "Northamptonshire",
        "country" => "en",
        "cpr" => "0123456789",
        "date_of_birth" => "1991-01-20",
        "notes" => "Some notes",
        "tags" => [
            [
                "id" => 15,
            ],
            [
                "name" => "Gold clients",
            ],
        ],
        "status" => "active",
        "custom_attributes" => [
            [
                "identifier" => "birthday",
                "value" => "1991-05-23",
            ],
        ],
        "danmark_group" => 1,
        "sync_to_mailchimp" => false,
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X PUT "http://localhost/api/v1/clients/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"name":"John Doe","email":"john.doe@example.com","ean":5789012345678,"phone":"+44 7380111233","address":"159 Bailey Brooks","address_line_2":"123 Main Street","zip":"M14 5PT","city":"Manchester","zone":"Northamptonshire","country":"en","cpr":"0123456789","date_of_birth":"1991-01-20","notes":"Some notes","tags":[{"id":15},{"name":"Gold clients"}],"status":"active","custom_attributes":[{"identifier":"birthday","value":"1991-05-23"}],"danmark_group":1,"sync_to_mailchimp":false}'

const url = new URL("http://localhost/api/v1/clients/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "name": "John Doe",
    "email": "john.doe@example.com",
    "ean": 5789012345678,
    "phone": "+44 7380111233",
    "address": "159 Bailey Brooks",
    "address_line_2": "123 Main Street",
    "zip": "M14 5PT",
    "city": "Manchester",
    "zone": "Northamptonshire",
    "country": "en",
    "cpr": "0123456789",
    "date_of_birth": "1991-01-20",
    "notes": "Some notes",
    "tags": [
        {
            "id": 15
        },
        {
            "name": "Gold clients"
        }
    ],
    "status": "active",
    "custom_attributes": [
        {
            "identifier": "birthday",
            "value": "1991-05-23"
        }
    ],
    "danmark_group": 1,
    "sync_to_mailchimp": false
}

fetch(url, {
    method: "PUT",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Updated",
    "data": {
        "id": 1,
        "name": "John Doe",
        "email": "john.doe@example.com",
        "ean": "5789012345678",
        "phone": "+44 7380111233",
        "address": "159 Bailey Brooks",
        "address_line_2": "123 Main Street",
        "zip": "M14 5PT",
        "city": "Manchester",
        "zone": "Northamptonshire",
        "country": "en",
        "alpha2": "GB",
        "cpr": "0123456789",
        "date_of_birth": "1991-01-20",
        "notes": "Some notes",
        "status": "active",
        "tags": [],
        "custom_attributes": [
            {
                "identifier": "birthday",
                "value": "1991-05-23"
            }
        ],
        "danmark_group": 1
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "name": [
            "The Name field is required."
        ]
    }
}

HTTP Request

PUT api/v1/clients/{client}

PATCH api/v1/clients/{client}

Body Parameters

Parameter Type Status Description
name string required The Name of the client.
email string optional The Email of the client.
ean string optional The EAN-number of the client.
phone string optional The Phone of the client with the country code. If the country code is omitted, the account's default country code is applied.
address string optional The Address of the client.
address_line_2 string optional The Address Line 2 of the client.
zip string optional The Zip/Postcode of the client.
city string optional The City of the client.
zone string optional The State/Province/County of the client.
country string optional The Country code of the client.
cpr string optional The CPR of the client.
date_of_birth The optional date of birth of the client in YYYY-MM-DD format.
notes string optional The Notes of the client.
tags array optional The tags associated to the client. It's possible to use the id or the name for each tag.
status string optional The status of the client. Currently only two statuses are allowed: "active" and "inactive".
custom_attributes array optional The custom fields and their values for the client. Must have "Custom Fields" app enabled. Non-existent fields will be ignored. See Client Custom Attributes for a list of available attributes on your account.
danmark_group integer optional (Danish accounts only) The Sygeforsikring "danmark" group number for this client. Must be one of these values: null (not set), 0 (Ved ikke), 1 (Gruppe 1), 2 (Gruppe 2), 5 (Gruppe 5) or 6 (Gruppe 6). A valid CPR is required to assign a client to a "danmark" group.
sync_to_mailchimp boolean optional (Requires Mailchimp app active) Whether to sync this client to Mailchimp.

Delete client


Requires authentication Removes the client from the user's account and all of its associated data, such as bookings/journals/messages. Use with caution.

Scope : clients-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->delete("api/v1/clients/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X DELETE "http://localhost/api/v1/clients/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/clients/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "DELETE",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Deleted"
}

HTTP Request

DELETE api/v1/clients/{client}

Client tags

List all client tags


Requires authentication Returns all of user's clients tags. Use the filter options provided to order the results. For example: GET api/v1/clients?order_by_tag=asc. Please note that this is an endpoint to administer the tags already created on the user. If you need to add/remove tags from clients, then use the Clients endpoint instead.

Scope: clients-read or clients-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/client-tags", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "order_by_name" => "asc",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/client-tags" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/client-tags");

    let params = {
            "order_by_name": "asc",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "name": "Premium clients",
            "timestamp": "2019-09-30T17:55:00+02:00"
        },
        {
            "id": 2,
            "name": "Gold clients",
            "timestamp": "2019-09-30T17:55:00+02:00"
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/client-tags

Query Parameters

Parameter Status Description
order_by_name optional =[asc|desc] Order results by name.
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Create client tag


Requires authentication Scope: clients-readwrite

Creates a new client tag with the given data.

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->post("api/v1/client-tags", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "name" => "Premium clients",
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X POST "http://localhost/api/v1/client-tags" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"name":"Premium clients"}'

const url = new URL("http://localhost/api/v1/client-tags");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "name": "Premium clients"
}

fetch(url, {
    method: "POST",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (201):

{
    "message": "The new tag was created",
    "data": {
        "id": 123,
        "name": "Premium clients",
        "timestamp": "2019-09-30T17:55:00+02:00"
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "name": [
            "The name field is required."
        ]
    }
}

HTTP Request

POST api/v1/client-tags

Body Parameters

Parameter Type Status Description
name string required The Name of the tag.

Show client tag


Requires authentication Returns the client tag data for a given ID.

Scope: clients-read or clients-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/client-tags/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/client-tags/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/client-tags/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "name": "Premium clients",
    "timestamp": "2019-09-30T17:55:00+02:00"
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/client-tags/{client_tag}

Update client tag


Requires authentication Updates the clients tag.

Scope: clients-readwrite`

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->put("api/v1/client-tags/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "name" => "Premium clients",
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X PUT "http://localhost/api/v1/client-tags/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"name":"Premium clients"}'

const url = new URL("http://localhost/api/v1/client-tags/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "name": "Premium clients"
}

fetch(url, {
    method: "PUT",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "The tag was updated",
    "data": {
        "id": 1,
        "name": "Premium clients",
        "timestamp": "2019-09-30T17:55:00+02:00"
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "name": [
            "The name field is required."
        ]
    }
}

HTTP Request

PUT api/v1/client-tags/{client_tag}

PATCH api/v1/client-tags/{client_tag}

Body Parameters

Parameter Type Status Description
name string required The Name of the tag.

Delete client tag


Requires authentication Removes the clients tag from the user's account.

Scope: clients-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->delete("api/v1/client-tags/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X DELETE "http://localhost/api/v1/client-tags/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/client-tags/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "DELETE",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Deleted"
}

HTTP Request

DELETE api/v1/client-tags/{client_tag}

Discount Codes

List discount codes


Requires authentication Returns all of user's discount codes. Data is only available if the app is active.

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/discount-codes", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/discount-codes" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/discount-codes");

    let params = {
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 122,
            "code": "1000off",
            "type": "value",
            "value": 1000,
            "value_formatted": "Value (DKK 1.000,00)",
            "number_of_uses": null,
            "client_use_limit": null,
            "expires_at": null,
            "is_expired": false,
            "is_available": true,
            "availability": {
                "all_calendars": false,
                "all_products": true,
                "all_events": true,
                "all_vouchers": false,
                "all_digital_content_packages": true,
                "all_gift_cards": false,
                "events": [],
                "event_groups": [],
                "products": [],
                "calendars": [
                    {
                        "id": 545,
                        "name": "Calendar 1",
                        "type": "standard"
                    }
                ],
                "digital_content_packages": []
            },
            "created_at": "2022-09-14T12:30:37+02:00",
            "updated_at": "2022-09-14T12:30:37+02:00"
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

Example response (403):

{
    "error": {
        "message": "Discount codes app is not active.",
        "status_code": 403
    }
}

HTTP Request

GET api/v1/discount-codes

Query Parameters

Parameter Status Description
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Show discount code


Requires authentication Returns the discount code data for a given code. Data is only available if the app is active.

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/discount-codes/1000off", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/discount-codes/1000off" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/discount-codes/1000off");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 122,
    "code": "1000off",
    "type": "value",
    "value": 1000,
    "value_formatted": "Value (DKK 1.000,00)",
    "number_of_uses": null,
    "client_use_limit": null,
    "expires_at": null,
    "is_expired": false,
    "is_available": true,
    "availability": {
        "all_calendars": false,
        "all_products": true,
        "all_events": true,
        "all_vouchers": false,
        "all_digital_content_packages": true,
        "all_gift_cards": false,
        "events": [],
        "event_groups": [],
        "products": [],
        "calendars": [
            {
                "id": 545,
                "name": "Calendar 1",
                "type": "standard"
            }
        ],
        "digital_content_packages": []
    },
    "created_at": "2022-09-14T12:30:37+02:00",
    "updated_at": "2022-09-14T12:30:37+02:00"
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

Example response (403):

{
    "error": {
        "message": "Discount codes app is not active.",
        "status_code": 403
    }
}

HTTP Request

GET api/v1/discount-codes/{code}

URL Parameters

Parameter Status Description
code required The discount code.

EasyPay

List payments


Requires authentication Returns all of user's easypay payments. Use the filter options provided to narrow down the results. For example: GET api/v1/easypay/payments?filter=paid

Scope : easypay-read

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/easypay/payments", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "filter" => "paid",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/easypay/payments" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/easypay/payments");

    let params = {
            "filter": "paid",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "amount": 100,
            "payment_date": "021-05-05T14:01:21+02:00",
            "status": "charged",
            "client_id": 123,
            "invoice_id": 123,
            "booking_id": 123,
            "transfer_id": 123,
            "service_id": 123,
            "invoice_number": 100002,
            "payment_card_type": "visa",
            "payment_card_last4": 1234,
            "card_fee": 0.92,
            "card_fee_vat": 0.12,
            "transaction_fee": 0.15,
            "transaction_fee_vat": 0.05,
            "total_fee": 1.24,
            "currency": "GBP"
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/easypay/payments

Query Parameters

Parameter Status Description
filter optional Filter results by status. Available values: "charge_failed", "reserved", "charged", "in_transit", "paid", "refunded"
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Show payment


Requires authentication Returns the payment data for a given ID.

Scope : easypay-read

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/easypay/payments/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/easypay/payments/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/easypay/payments/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "amount": 100,
    "payment_date": "021-05-05T14:01:21+02:00",
    "status": "charged",
    "client_id": 123,
    "invoice_id": 123,
    "booking_id": 123,
    "transfer_id": 123,
    "service_id": 123,
    "invoice_number": 100002,
    "payment_card_type": "visa",
    "payment_card_last4": 1234,
    "card_fee": 0.92,
    "card_fee_vat": 0.12,
    "transaction_fee": 0.15,
    "transaction_fee_vat": 0.05,
    "total_fee": 1.24,
    "currency": "GBP"
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/easypay/payments/{payment}

List transfers


Requires authentication Returns all of user's easypay transfers. Use the filter options provided to narrow down the results. For example: GET api/v1/easypay/payments?filter=paid

Scope : easypay-read

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/easypay/transfers", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/easypay/transfers" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/easypay/transfers");

    let params = {
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "status": "in_transit",
            "amount": 200,
            "transfer_id": 21342,
            "transfer_date": "2021-04-03 10:04:20",
            "payments": [
                {
                    "id": 1,
                    "amount": 100,
                    "payment_date": "021-05-05T14:01:21+02:00",
                    "status": "charged",
                    "client_id": 123,
                    "invoice_id": 123,
                    "booking_id": 123,
                    "transfer_id": 123,
                    "service_id": 123,
                    "invoice_number": 100002,
                    "payment_card_type": "visa",
                    "payment_card_last4": 1234,
                    "card_fee": 0.92,
                    "card_fee_vat": 0.12,
                    "transaction_fee": 0.15,
                    "transaction_fee_vat": 0.05,
                    "total_fee": 1.24,
                    "currency": "GBP"
                }
            ]
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/easypay/transfers

Query Parameters

Parameter Status Description
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Show transfer


Requires authentication Returns the transfer data for a given ID.

Scope : easypay-read

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/easypay/transfers/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/easypay/transfers/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/easypay/transfers/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "status": "in_transit",
    "amount": 200,
    "transfer_id": 21342,
    "transfer_date": "2021-04-03 10:04:20",
    "payments": [
        {
            "id": 1,
            "amount": 100,
            "payment_date": "021-05-05T14:01:21+02:00",
            "status": "charged",
            "client_id": 123,
            "invoice_id": 123,
            "booking_id": 123,
            "transfer_id": 123,
            "service_id": 123,
            "invoice_number": 100002,
            "payment_card_type": "visa",
            "payment_card_last4": 1234,
            "card_fee": 0.92,
            "card_fee_vat": 0.12,
            "transaction_fee": 0.15,
            "transaction_fee_vat": 0.05,
            "total_fee": 1.24,
            "currency": "GBP"
        }
    ]
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/easypay/transfers/{transfer}

Invoices

List invoices


Requires authentication Returns all of user's invoices. Use the filter options provided to narrow down the results. For example: GET api/v1/invoices?client_id=123

Scope : invoices-read or invoices-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/invoices", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "id" => "2135",
            "number" => "20020",
            "client_id" => "54347",
            "paid" => "1",
            "unpaid" => "1",
            "order_by_number" => "asc",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/invoices" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/invoices");

    let params = {
            "id": "2135",
            "number": "20020",
            "client_id": "54347",
            "paid": "1",
            "unpaid": "1",
            "order_by_number": "asc",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "draft": false,
            "client_id": 123,
            "number": 454,
            "date": "2019-04-20T00:00:00+01:00",
            "due_date": "2019-04-20T00:00:00+01:00",
            "executed_date": "2019-04-20T00:00:00+01:00",
            "paid_date": "2019-04-20T00:00:00+01:00",
            "sent": true,
            "sent_via_ean": false,
            "employee": null,
            "paid": true,
            "credited": false,
            "status": "paid",
            "client_name": "John Doe",
            "client_cpr": "123456-7890",
            "client_address": "54 Harry Lane",
            "client_address_line_2": "Kastanievej 17",
            "client_zip": "1234",
            "client_city": "Copenhagen",
            "client_zone": "Region Sjælland",
            "client_country": "dk",
            "note": "Lorem Ipsum",
            "payment_type": "card",
            "currency": "DKK",
            "total": 625,
            "lines": [
                {
                    "id": 23,
                    "invoice_id": 1,
                    "product_id": 1,
                    "position": 0,
                    "amount": 1,
                    "price": 500,
                    "vat": true,
                    "vat_rate": 25,
                    "total_without_vat": 500,
                    "total": 625,
                    "text": "Client appointment",
                    "comment": "",
                    "booking_id": 2,
                    "booking_description": "Appointment Tuesday 16 April 2019, 16:05",
                    "event_signup_id": null,
                    "client_voucher_id": null
                }
            ]
        },
        {
            "id": 2,
            "draft": false,
            "client_id": 22,
            "number": 455,
            "date": "2019-05-01T00:00:00+01:00",
            "due_date": "2019-05-01T00:00:00+01:00",
            "executed_date": "2019-05-01T00:00:00+01:00",
            "paid_date": "2019-05-01T00:00:00+01:00",
            "sent": true,
            "sent_via_ean": false,
            "employee": {
                "id": 12345,
                "name": "Jane Doe",
                "email": "janedoe@example.com",
                "profile_image_url": "..."
            },
            "paid": true,
            "credited": false,
            "status": "paid",
            "client_name": "Jaimes Armsworth",
            "client_cpr": "121212-7890",
            "client_address": "4 Snow Street",
            "client_address_line_2": "Kastanievej 17",
            "client_zip": "5454",
            "client_city": "Copenhagen",
            "client_zone": "Region Sjælland",
            "client_country": "dk",
            "note": "Lorem Ipsum",
            "payment_type": "card",
            "currency": "DKK",
            "total": 1500,
            "lines": [
                {
                    "...": "..."
                },
                {
                    "...": "..."
                }
            ]
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/invoices

Query Parameters

Parameter Status Description
id optional Return only invoices with this specific ID.
number optional Return only invoices with this specific invoice number.
client_id optional Return only invoices belonging to this client ID.
paid optional Return only the paid invoices.
unpaid optional Return only the unpaid invoices.
order_by_number optional =[asc|desc] Order results by invoice number.
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Mark an invoice as paid


Requires authentication Scope : invoices-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->post("api/v1/invoices/1/mark-as-paid", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "date" => "2019-05-16",
        "payment_type" => "card",
        "amount" => 1500,
        "currency" => "DKK",
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X POST "http://localhost/api/v1/invoices/1/mark-as-paid" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"date":"2019-05-16","payment_type":"card","amount":1500,"currency":"DKK"}'

const url = new URL("http://localhost/api/v1/invoices/1/mark-as-paid");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "date": "2019-05-16",
    "payment_type": "card",
    "amount": 1500,
    "currency": "DKK"
}

fetch(url, {
    method: "POST",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "The Invoice 123 was marked as paid on 2019-05-20 with the payment method Card"
}

Example response (400):

{
    "error": {
        "message": "The Invoice 123 is already paid.",
        "status_code": 400
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "payment_type": [
            "The selected Payment type is invalid."
        ]
    }
}

HTTP Request

POST api/v1/invoices/{id}/mark-as-paid

Body Parameters

Parameter Type Status Description
date date required The Date of the payment.
payment_type string required The Payment method used. Available values: 'transfer', 'cash', 'card', 'other', 'mobile_pay' (Denmark only), 'health_insurance', 'swish' (Sweden only)
amount float required The Amount of the payment.
currency string required The Currency of the payment.

Send Invoice

Sends an invoice via e-mail. Tags are supported in the "text" field. See the "Email and SMS" settings page in the system to find out which tags are supported for invoice emails.

Scope : invoices-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->post("api/v1/invoices/1/send", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "subject" => "Invoice",
        "text" => "Hi, please see the attached invoice.",
        "email" => "john.doe@example.com",
        "as_attachment" => true,
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X POST "http://localhost/api/v1/invoices/1/send" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"subject":"Invoice","text":"Hi, please see the attached invoice.","email":"john.doe@example.com","as_attachment":1}'

const url = new URL("http://localhost/api/v1/invoices/1/send");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "subject": "Invoice",
    "text": "Hi, please see the attached invoice.",
    "email": "john.doe@example.com",
    "as_attachment": 1
}

fetch(url, {
    method: "POST",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Invoice 1 was sent to John Doe (Test klient) (john.doe@example.com)",
    "data": {
        "id": 1,
        "draft": false,
        "client_id": 123,
        "number": 454,
        "date": "2019-04-20T00:00:00+01:00",
        "due_date": "2019-04-20T00:00:00+01:00",
        "executed_date": "2019-04-20T00:00:00+01:00",
        "paid_date": "2019-04-20T00:00:00+01:00",
        "sent": true,
        "sent_via_ean": false,
        "employee": null,
        "paid": true,
        "credited": false,
        "status": "paid",
        "client_name": "John Doe",
        "client_cpr": "123456-7890",
        "client_address": "54 Harry Lane",
        "client_address_line_2": "Kastanievej 17",
        "client_zip": "1234",
        "client_city": "Copenhagen",
        "client_zone": "Region Sjælland",
        "client_country": "dk",
        "note": "Lorem Ipsum",
        "payment_type": "card",
        "currency": "DKK",
        "total": 625,
        "lines": [
            {
                "...": "..."
            }
        ]
    }
}

Example response (422):

{
    "error": {
        "message": "The given data is invalid",
        "status_code": 422
    },
    "data": {
        "email": [
            "The email must be a valid email address."
        ]
    }
}

HTTP Request

POST api/v1/invoices/{id}/send

Body Parameters

Parameter Type Status Description
subject string required The subject of the email.
text string required The body of the email.
email string required The email address.
as_attachment boolean optional Whether to attach the invoice PDF to the email.

Online Booking

List Available Dates


Requires authentication Returns a list of Available dates based on a supplied date range, products and calendars. It does not return dates in the past, as they would never be available.

Scope: calendars-read or calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/online-booking/available-dates", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "from" => "2020-08-01",
            "to" => "2020-09-01",
            "product_ids" => "1",
            "addon_ids" => "4",
            "calendar_id" => "1",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/online-booking/available-dates" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/online-booking/available-dates");

    let params = {
            "from": "2020-08-01",
            "to": "2020-09-01",
            "product_ids": "1",
            "addon_ids": "4",
            "calendar_id": "1",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "date": "2020-08-21",
            "available": true,
            "available_in_calendars": [
                {
                    "id": 1,
                    "name": "Test calendar",
                    "description": "",
                    "color": "#C3E1FF",
                    "type": "standard"
                }
            ]
        },
        {
            "date": "2020-08-22",
            "available": false,
            "reason": "closed_opening_time"
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/online-booking/available-dates

Query Parameters

Parameter Status Description
from required date_format:Y-m-d Return Available Dates from the start of the supplied date.
to required date_format:Y-m-d Return Available Dates to the end of the supplied date.
product_ids required array Return Available Dates/Times based on an array of product IDs.
addon_ids optional array Return Available Dates/Times based on an array of product addons that are allowed to be attached to the supplied product_ids.
calendar_id optional integer Return Available Dates/Times based on a specific Calendar.
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

List Available Times


Requires authentication Returns a list of Available times based on a supplied date, products and calendars. It does not return times in the past, as they would never be available.

Scope: calendars-read or calendars-readwrite

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/online-booking/available-times", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "date" => "2020-09-01",
            "product_ids" => "1",
            "addon_ids" => "4",
            "calendar_id" => "1",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/online-booking/available-times" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/online-booking/available-times");

    let params = {
            "date": "2020-09-01",
            "product_ids": "1",
            "addon_ids": "4",
            "calendar_id": "1",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "date": "2020-08-30T09:00:00+01:00",
            "available": true,
            "available_in_calendars": [
                {
                    "id": 1,
                    "name": "Test calendar",
                    "description": "",
                    "color": "#C3E1FF",
                    "type": "standard"
                }
            ]
        },
        {
            "date": "2020-08-30T10:00:00+01:00",
            "available": false,
            "reason": "overlapping_booking"
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/online-booking/available-times

Query Parameters

Parameter Status Description
date required date_format:Y-m-d Return Available Times for the supplied date.
product_ids required array Return Available Dates/Times based on an array of product IDs.
addon_ids optional array Return Available Dates/Times based on an array of product addons that are allowed to be attached to the supplied product_ids.
calendar_id optional integer Return Available Dates/Times based on a specific Calendar.
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Services

List service groups


Requires authentication Returns all of user's service groups.

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/service-groups", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/service-groups" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/service-groups");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

[
    {
        "id": 1,
        "name": "Service Group Name 1"
    },
    {
        "id": 2,
        "name": "Service Group Name 2"
    }
]

HTTP Request

GET api/v1/service-groups

List services


Requires authentication Returns all of user's services. Use the order param to order the results. For example: GET api/v1/services?order_by_name=asc

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/services", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "order_by_name" => "asc",
            "product_group_id" => "1",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/services" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/services");

    let params = {
            "order_by_name": "asc",
            "product_group_id": "1",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "name": "Example of a service",
            "description": "This is an example of your first service. You can either edit this service or delete it and create a new one to make if fit your practice",
            "duration": 45,
            "break_after_service": 15,
            "currency": "DKK",
            "price": 50,
            "vat": true,
            "vat_rate": 25,
            "product_group_id": 1,
            "product_group": {
                "id": 1,
                "name": "Service Group Name 1"
            },
            "is_available_in_online_booking": true,
            "online_payment_enabled": true,
            "reserve_online_payment": true,
            "require_payment_in_advance": true,
            "payment_rates_enabled": true,
            "payment_rates_interval": "weekly",
            "payment_rates_number": 4,
            "order": 1,
            "calendars": [
                {
                    "id": 12,
                    "name": "Calendar",
                    "color": "#C3E1FF"
                }
            ],
            "addons": [
                {
                    "id": 2,
                    "name": "Additional time"
                }
            ]
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/services

Query Parameters

Parameter Status Description
order_by_name optional =[asc|desc] Order results by name.
product_group_id optional (optional) Filter results by product group ID.
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Show service


Requires authentication Returns the service data for a given ID.

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/services/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/services/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/services/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "name": "Example of a service",
    "description": "This is an example of your first service. You can either edit this service or delete it and create a new one to make if fit your practice",
    "duration": 45,
    "break_after_service": 15,
    "currency": "DKK",
    "price": 50,
    "vat": true,
    "vat_rate": 25,
    "product_group_id": 1,
    "product_group": {
        "id": 1,
        "name": "Service Group Name 1"
    },
    "is_available_in_online_booking": true,
    "online_payment_enabled": true,
    "reserve_online_payment": true,
    "require_payment_in_advance": true,
    "payment_rates_enabled": true,
    "payment_rates_interval": "weekly",
    "payment_rates_number": 4,
    "order": 1,
    "calendars": [
        {
            "id": 12,
            "name": "Calendar",
            "color": "#C3E1FF"
        }
    ],
    "addons": [
        {
            "id": 2,
            "name": "Additional time"
        }
    ]
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/services/{product}

List addons


Requires authentication Returns all of user's addons. Addons are similar to services, but have fewer properties and their functionality depends on the service it is attached to during the booking (Online Payment, Payment Rates, etc). Use the filter options provided to narrow down the results. For example: GET api/v1/addons?order_by_name=asc

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/addons", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "order_by_name" => "asc",
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/addons" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/addons");

    let params = {
            "order_by_name": "asc",
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 2,
            "name": "Additional time",
            "description": "",
            "duration": 15,
            "break_after_service": 0,
            "currency": "DKK",
            "price": 15,
            "vat": true,
            "vat_rate": 25,
            "is_available_in_online_booking": true,
            "services": [
                {
                    "id": 1,
                    "name": "Example of a service"
                }
            ]
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/addons

Query Parameters

Parameter Status Description
order_by_name optional =[asc|desc] Order results by name.
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Show addon


Requires authentication Returns the addon data for a given ID.

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/addons/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/addons/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/addons/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 2,
    "name": "Additional time",
    "description": "",
    "duration": 15,
    "break_after_service": 0,
    "currency": "DKK",
    "price": 15,
    "vat": true,
    "vat_rate": 25,
    "is_available_in_online_booking": true,
    "services": [
        {
            "id": 1,
            "name": "Example of a service"
        }
    ]
}

Example response (404):

{
    "error": {
        "message": "Not Found",
        "status_code": 404
    }
}

HTTP Request

GET api/v1/addons/{addon}

User

Show user information


Requires authentication Returns information about the authenticated user

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/user", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/user" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/user");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "name": "Name of user",
    "email": "E-mail of the user",
    "phone": "Phone number of the user",
    "country": "Country of the user",
    "vat_number": "VAT number of the user",
    "address": "Address of the user",
    "zip_code": "Zip code of the user",
    "city": "City of the user"
}

HTTP Request

GET api/v1/user

User Employees

List Employees


Requires authentication Returns all of user's employees. For example: GET api/v1/employees

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/employees", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'query' => [
            "page_size" => "20",
            "page" => "4",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/employees" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/employees");

    let params = {
            "page_size": "20",
            "page": "4",
        };
    Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "data": [
        {
            "id": 1,
            "name": "John Doe",
            "email": "john@example.com",
            "type": "employee",
            "profile_image_url": "https:\/\/www.gravatar.com\/avatar\/a6753b5d10e6a069e284cff387f94665?d=mm",
            "2fa_enabled": false
        },
        {
            "id": 2,
            "name": "Jane Doe",
            "email": "jane@example.com",
            "type": "tenant",
            "profile_image_url": "https:\/\/www.gravatar.com\/avatar\/a6753b5d10e6a069e284cff387f94665?d=mm",
            "2fa_enabled": true
        }
    ],
    "links": {
        "...": "..."
    },
    "meta": {
        "...": "..."
    }
}

HTTP Request

GET api/v1/employees

Query Parameters

Parameter Status Description
page_size optional (default: 10) The number of items per page.
page optional (default: 1) The page number.

Show Employee


Requires authentication Returns an Employee. For example: GET api/v1/employees/1

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/employees/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/employees/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/employees/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com",
    "type": "employee",
    "profile_image_url": "https:\/\/www.gravatar.com\/avatar\/a6753b5d10e6a069e284cff387f94665?d=mm",
    "2fa_enabled": false
}

HTTP Request

GET api/v1/employees/{employee}

Webhooks

List webhooks


Requires authentication Returns all webhooks

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/webhooks", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/webhooks" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/webhooks");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

[
    {
        "id": "1",
        "name": "Example webhook",
        "method": "POST",
        "url": "http:\/\/test.example.com",
        "events": [
            "client-created",
            "client-updated"
        ]
    },
    {
        "id": "2",
        "name": "Example webhook 2",
        "method": "POST",
        "url": "http:\/\/test.example2.com",
        "events": [
            "booking-created"
        ]
    }
]

HTTP Request

GET api/v1/webhooks

Create a webhook


Requires authentication

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->post("api/v1/webhooks", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "name" => "Example webhook",
        "url" => "http://test.example.com",
        "method" => "POST",
        "events" => [
            "0" => "client-created",
            "1" => "client-updated",
        ],
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X POST "http://localhost/api/v1/webhooks" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"name":"Example webhook","url":"http:\/\/test.example.com","method":"POST","events":["client-created","client-updated"]}'

const url = new URL("http://localhost/api/v1/webhooks");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "name": "Example webhook",
    "url": "http:\/\/test.example.com",
    "method": "POST",
    "events": [
        "client-created",
        "client-updated"
    ]
}

fetch(url, {
    method: "POST",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Created",
    "data": {
        "id": "1",
        "name": "Example webhook",
        "method": "POST",
        "url": "http:\/\/test.example.com",
        "events": [
            "client-created",
            "client-updated"
        ]
    }
}

HTTP Request

POST api/v1/webhooks

Body Parameters

Parameter Type Status Description
name string required Name of the webhook.
url string required URL of the webhook.
method string required Request method for the webhook. Currently only POST is supported.
events array required An array of supported events, which will trigger this webhook: booking-created, booking-updated, booking-deleted, client-created, client-updated.

Retrieve a webhook


Requires authentication Return the webhook for given ID

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->get("api/v1/webhooks/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X GET -G "http://localhost/api/v1/webhooks/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/webhooks/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "GET",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "id": "1",
    "name": "Example webhook",
    "method": "POST",
    "url": "http:\/\/test.example.com",
    "events": [
        "client-created",
        "client-updated"
    ]
}

HTTP Request

GET api/v1/webhooks/{webhook}

URL Parameters

Parameter Status Description
id required ID of the webhook.

Delete a webhook


Requires authentication

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->delete("api/v1/webhooks/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X DELETE "http://localhost/api/v1/webhooks/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}"
const url = new URL("http://localhost/api/v1/webhooks/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

fetch(url, {
    method: "DELETE",
    headers: headers,
})
    .then(response => response.json())
    .then(json => console.log(json));

HTTP Request

DELETE api/v1/webhooks/{webhook}

URL Parameters

Parameter Status Description
id required ID of the webhook

Update a webhook


Requires authentication

Example request:


$client = new \GuzzleHttp\Client();
$response = $client->put("api/v1/webhooks/1", [
    'headers' => [
            "Content-Type" => "application/json",
            "Accept" => "application/json",
            "Authorization" => "Bearer {token}",
        ],
    'json' => [
        "name" => "Example webhook",
        "url" => "http://test.example.com",
        "method" => "POST",
        "events" => [
            "0" => "client-created",
            "1" => "client-updated",
        ],
    ],
]);
$body = $response->getBody();
print_r(json_decode((string) $body));
curl -X PUT "http://localhost/api/v1/webhooks/1" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Authorization: Bearer {token}" \
    -d '{"name":"Example webhook","url":"http:\/\/test.example.com","method":"POST","events":["client-created","client-updated"]}'

const url = new URL("http://localhost/api/v1/webhooks/1");

let headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer {token}",
}

let body = {
    "name": "Example webhook",
    "url": "http:\/\/test.example.com",
    "method": "POST",
    "events": [
        "client-created",
        "client-updated"
    ]
}

fetch(url, {
    method: "PUT",
    headers: headers,
    body: body
})
    .then(response => response.json())
    .then(json => console.log(json));

Example response (200):

{
    "message": "Updated",
    "data": {
        "id": "1",
        "name": "Example webhook",
        "method": "POST",
        "url": "http:\/\/test.example.com",
        "events": [
            "client-created",
            "client-updated"
        ]
    }
}

HTTP Request

PUT api/v1/webhooks/{webhook}

URL Parameters

Parameter Status Description
id required ID of the webhook.

Body Parameters

Parameter Type Status Description
name string required Name of the webhook.
url string required URL of the webhook.
method string required Request method for the webhook. Currently only POST is supported.
events array required An array of supported events, which will trigger this webhook: booking-created, booking-updated, booking-deleted, client-created, client-updated.

Errors

The EasyPractice API uses the following error codes:

Error Code Meaning
400 Bad Request -- Your request is malformed or otherwise invalid. Please double check our API documentation for allowed request parameters.
401 Unauthorized -- Your API key is incorrect or has been revoked. Check the Authentication section on how to get an API token.
403 Forbidden -- You do not have access rights to the resource. Your API key might be limited to what you can access.
404 Not Found -- The resource was not found.
405 Method Not Allowed -- The HTTP method you used was incorrect and unsupported for the specific endpoint.
422 Unprocessable Entity -- Some or all data provided is invalid for the type of request. Please see the response data for clues about which of the data provided is invalid and why.
429 Too Many Requests -- You are making too many requests.
500 Internal Server Error -- We had a problem with our server. Try again later.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.

Help

If you have any questions regarding our API, you can contact us at api@easypractice.net.

EasyPractice © 2021