MENU navbar-image

Introduction

LoopSpark Public API

This documentation aims to provide all the information you need to work with our API.

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {YOUR_FRANCHISE_TOKEN}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

To obtain a token, please contact our support at support@loopspark.com.

Authorization

To authorize your requests, you need to pass a token in the request header Authorization.

Example: `Authorization: Bearer YourToken`

Retrieving your franchise

requires authentication

This endpoint should be used to make sure that the your authorization token is valid. It will return an object with the name of the franchise.

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/me" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/me"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/me';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "name": "Your franchise name"
    },
    "meta": {
        "success": true
    }
}
 

Example response (403):


{
    "message": "Unauthenticated."
}
 

Request      

GET api/franchise/v1/me

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Integrations - MindBODY

requires authentication

This is the first step required in order to activate a SiteID.

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/21225/integrations/mbo/authorization-link" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/integrations/mbo/authorization-link"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/integrations/mbo/authorization-link';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "authorization_link": "https://...."
}
 

Example response (406, Studio already integrated):


{
   "error"   : TRUE,
   "message" : "The SiteID is already integrated"
}
 

Confirm that the authorization is completed

requires authentication

This is the second and final step to activate a SiteID. After the authorization link is created (using GET authorization-link), proceed to activate the SiteID on MINDBODY. Once it's active, confirm it to LoopSpark by using this POST request. The SiteID needs to have Locations already available on MINDBODY (beside the "Online Store")

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/integrations/mbo/authorization-completed" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/integrations/mbo/authorization-completed"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/integrations/mbo/authorization-completed';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
   "ok" : TRUE
}
 

Request      

POST api/franchise/v1/{siteId}/integrations/mbo/authorization-completed

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID Example: 21225

Automation

All the Endpoints regarding Automations of your franchise

Automations list

requires authentication

Retrieve a list of all the Automations available. The results are paginated

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/automations?replicas_of_id=123&include_paused=1&include_archived=1&only_primaries=1&per_page=10&only_type=SMS&sort_by=id&sort_direction=asc&site_id=12345&category=Welcome+Sequence" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/automations"
);

const params = {
    "replicas_of_id": "123",
    "include_paused": "1",
    "include_archived": "1",
    "only_primaries": "1",
    "per_page": "10",
    "only_type": "SMS",
    "sort_by": "id",
    "sort_direction": "asc",
    "site_id": "12345",
    "category": "Welcome Sequence",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/automations';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'replicas_of_id' => '123',
            'include_paused' => '1',
            'include_archived' => '1',
            'only_primaries' => '1',
            'per_page' => '10',
            'only_type' => 'SMS',
            'sort_by' => 'id',
            'sort_direction' => 'asc',
            'site_id' => '12345',
            'category' => 'Welcome Sequence',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": 31,
            "team_id": "6316",
            "active": false,
            "archived": false,
            "disabled": false,
            "is_primary": false,
            "replica_of": null,
            "message": {
                "subject": "eius et animi",
                "content": "Et fugiat sunt nihil accusantium. Mollitia modi deserunt aut ab provident perspiciatis quo. Nostrum aut adipisci quidem nostrum.\n\nIncidunt iure odit et et modi ipsum. Omnis autem et consequatur.\n\nEnim non facere tempora ex voluptatem laboriosam praesentium. Adipisci molestias fugit deleniti distinctio eum doloremque id. Libero aliquam veniam corporis dolorem mollitia deleniti."
            },
            "settings": {
                "name": "architecto",
                "category": null,
                "delivery_type": "SMS",
                "one_off": false,
                "send_window": {
                    "immediate": true,
                    "start_mins": null,
                    "end_mins": null
                },
                "start_at": null
            },
            "audience": "sint aut molestiae",
            "created_at": "2026-10-03T08:00:35+00:00",
            "updated_at": "2026-10-03T08:00:35+00:00",
            "last_ran_at": null,
            "tags": {
                "tags": [],
                "add": 1
            },
            "todo": {
                "overdue_hours": 0,
                "postponed_hours": null,
                "priority": "LOW",
                "tags": []
            },
            "stats": {
                "bounced": 0,
                "clicked": 0,
                "delivered": 0,
                "opened": 0,
                "sent": 0,
                "unsubscribe_clicked": 0
            }
        },
        {
            "id": 32,
            "team_id": "8222",
            "active": false,
            "archived": false,
            "disabled": false,
            "is_primary": false,
            "replica_of": null,
            "message": {
                "subject": "suscipit doloribus fugiat",
                "content": "Deserunt et error neque recusandae et. Dolorem et ut dicta. Assumenda consequatur ut et sunt quisquam. Repellendus ut eaque alias ratione dolores.\n\nEa ut aut deserunt sint quis. Quod id aspernatur consectetur id a consectetur assumenda. Neque sit sunt nihil accusantium odit ut perspiciatis.\n\nAut quis ut dolores omnis et earum. Asperiores est vel id aut officiis eos. Et qui unde et."
            },
            "settings": {
                "name": "sunt",
                "category": null,
                "delivery_type": "SMS",
                "one_off": false,
                "send_window": {
                    "immediate": true,
                    "start_mins": null,
                    "end_mins": null
                },
                "start_at": null
            },
            "audience": "sed quasi et",
            "created_at": "2026-10-03T08:00:35+00:00",
            "updated_at": "2026-10-03T08:00:35+00:00",
            "last_ran_at": null,
            "tags": {
                "tags": [],
                "add": 1
            },
            "todo": {
                "overdue_hours": 0,
                "postponed_hours": null,
                "priority": "LOW",
                "tags": []
            },
            "stats": {
                "bounced": 0,
                "clicked": 0,
                "delivered": 0,
                "opened": 0,
                "sent": 0,
                "unsubscribe_clicked": 0
            }
        }
    ]
}
 

Request      

GET api/franchise/v1/automations

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Query Parameters

replicas_of_id   number  optional    

Only show replica Automations of a specific Automation ID in the list. Default: null. Example: 123

include_paused   boolean  optional    

Include paused/inactive Automations in the list. Default: false. Example: true

include_archived   boolean  optional    

Include archived Automations in the list. Default: false. Example: true

only_primaries   boolean  optional    

Only show primary Automations in the list. Default: false. Example: true

per_page   number  optional    

The amount of automations to return. Max: 250. Default: 250. Must be at least 1. Must not be greater than 250. Example: 10

only_type   string  optional    

Only show Automations using a specific medium. Values are EMAIL,SMS,TAG or TODO. Default: null. Example: SMS

Must be one of:
  • EMAIL
  • SMS
  • TODO
  • TAG
  • EXPORT
sort_by   string  optional    

Sorts the results by either id, name, last_ran_at, or creation_date. Default: id. Example: id

Must be one of:
  • id
  • name
  • creation_date
  • last_ran_at
sort_direction   string  optional    

Sorts the results asc for ascending order, desc for descending order. Default: asc. Example: asc

Must be one of:
  • asc
  • desc
site_id   string  optional    

Limit the search to a specific SiteID. Default: null. Example: 12345

category   string  optional    

Limit the search to a specific category. Default: null. Example: Welcome Sequence

Response

Response Fields

stats   object     

This field includes the all-time statistics for the automation. The format changes depending on the type of automation.

Single Automation

requires authentication

Show a single Automation by ID

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/automations/123" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/automations/123"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/automations/123';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": 33,
        "team_id": "2131",
        "active": false,
        "archived": false,
        "disabled": false,
        "is_primary": false,
        "replica_of": null,
        "message": {
            "subject": "adipisci quidem nostrum",
            "content": "Incidunt iure odit et et modi ipsum. Omnis autem et consequatur.\n\nEnim non facere tempora ex voluptatem laboriosam praesentium. Adipisci molestias fugit deleniti distinctio eum doloremque id. Libero aliquam veniam corporis dolorem mollitia deleniti.\n\nQuia officia est dignissimos neque. Odio veritatis excepturi doloribus delectus fugit qui repudiandae. Est alias tenetur ratione. Voluptate accusamus ut et recusandae."
        },
        "settings": {
            "name": "aut",
            "category": null,
            "delivery_type": "TODO",
            "one_off": false,
            "send_window": {
                "immediate": true,
                "start_mins": null,
                "end_mins": null
            },
            "start_at": null
        },
        "audience": "eaque alias ratione",
        "created_at": "2026-10-03T08:00:35+00:00",
        "updated_at": "2026-10-03T08:00:35+00:00",
        "last_ran_at": null,
        "tags": {
            "tags": [],
            "add": 1
        },
        "todo": {
            "overdue_hours": 0,
            "postponed_hours": null,
            "priority": "LOW",
            "tags": []
        },
        "stats": {
            "completed": 0,
            "created": 0,
            "deleted": 0,
            "open": 0,
            "postponed": 0
        }
    }
}
 

Request      

GET api/franchise/v1/automations/{id}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

id   string     

The ID of the Automation Example: 123

Update an Automation

requires authentication

Update an Automation by ID

Example request:
curl --request PUT \
    "https://loopspark.com/api/franchise/v1/automations/1234" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"active\": true,
    \"archived\": true,
    \"name\": \"bngzmiyvdljnikhw\",
    \"category_name\": \"aykcmyuwpwlvqwrs\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/automations/1234"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "active": true,
    "archived": true,
    "name": "bngzmiyvdljnikhw",
    "category_name": "aykcmyuwpwlvqwrs"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/automations/1234';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'active' => true,
            'archived' => true,
            'name' => 'bngzmiyvdljnikhw',
            'category_name' => 'aykcmyuwpwlvqwrs',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": 34,
        "team_id": "4680",
        "active": false,
        "archived": false,
        "disabled": false,
        "is_primary": false,
        "replica_of": null,
        "message": {
            "subject": null,
            "content": null
        },
        "settings": {
            "name": "et",
            "category": null,
            "delivery_type": "TAG",
            "one_off": false,
            "send_window": {
                "immediate": true,
                "start_mins": null,
                "end_mins": null
            },
            "start_at": null
        },
        "audience": "consectetur assumenda eaque",
        "created_at": "2026-10-03T08:00:35+00:00",
        "updated_at": "2026-10-03T08:00:35+00:00",
        "last_ran_at": null,
        "tags": {
            "tags": [
                "ciao",
                "second"
            ],
            "add": 1
        },
        "todo": {
            "overdue_hours": 0,
            "postponed_hours": 0,
            "priority": "LOW",
            "tags": []
        },
        "stats": {
            "created": 0
        }
    }
}
 

Request      

PUT api/franchise/v1/automations/{id}

PATCH api/franchise/v1/automations/{id}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

id   string     

The ID of the Automation Example: 1234

Body Parameters

active   boolean  optional    

Example: true

archived   boolean  optional    

Example: true

name   string  optional    

Must be at least 1 character. Example: bngzmiyvdljnikhw

category_name   string  optional    

Must be at least 1 character. Example: aykcmyuwpwlvqwrs

Stats

requires authentication

Get the stats of a single automation

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/automations/123/stats?start_date=1581418800&end_date=1581678000" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/automations/123/stats"
);

const params = {
    "start_date": "1581418800",
    "end_date": "1581678000",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/automations/123/stats';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'start_date' => '1581418800',
            'end_date' => '1581678000',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": 123,
        "team_id": "foobar",
        "delivery_type": "EMAIL",
        "sent": 20,
        "bounced": 1,
        "delivered": 19,
        "opened": 0,
        "clicked": 0
    }
}
 

Example response (200):


{
    "data": {
        "id": 123,
        "team_id": "foobar",
        "delivery_type": "SMS",
        "sent": 20,
        "bounced": 1,
        "delivered": 19,
        "opened": 0,
        "clicked": 0
    }
}
 

Example response (200):


{
    "data": {
        "id": 123,
        "team_id": "foobar",
        "delivery_type": "TODO",
        "created": 100,
        "completed": 25,
        "open": 40,
        "postponed": 35,
        "deleted": 5
    }
}
 

Example response (200):


{
    "data": {
        "id": 123,
        "team_id": "foobar",
        "delivery_type": "TAG",
        "created": 100
    }
}
 

Request      

GET api/franchise/v1/automations/{automation_id}/stats

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

automation_id   string     

The ID of the Automation Example: 123

Query Parameters

start_date   string  optional    

The Unix timestamp of the starting date for the stats. Defaults to 7 days ago. Must be a valid date in the format U. Example: 1581418800

end_date   string  optional    

The Unix timestamp of the ending date for the stats. Defaults to now. Must be a valid date in the format U. Example: 1581678000

Action history

requires authentication

Show the most recent actions performed by an automation for a specific client.

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/automations/123/actions/12345?start_date=1581418800&end_date=1581678000" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/automations/123/actions/12345"
);

const params = {
    "start_date": "1581418800",
    "end_date": "1581678000",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/automations/123/actions/12345';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'start_date' => '1581418800',
            'end_date' => '1581678000',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "type": "EMAIL",
            "action": {
                "delivery_type": "EMAIL",
                "sent": false,
                "bounced": false,
                "delivered": false,
                "opened": false,
                "clicked": false,
                "last_contacted_at": "2026-10-03T08:00:35+00:00",
                "closed": false
            }
        },
        {
            "type": "EMAIL",
            "action": {
                "delivery_type": "EMAIL",
                "sent": false,
                "bounced": false,
                "delivered": false,
                "opened": false,
                "clicked": false,
                "last_contacted_at": "2026-10-03T08:00:35+00:00",
                "closed": false
            }
        }
    ]
}
 

Request      

GET api/franchise/v1/automations/{automation_id}/actions/{client_id}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

automation_id   string     

The ID of the Automation Example: 123

client_id   string     

The ID of the Client Example: 12345

Query Parameters

start_date   string  optional    

The Unix timestamp of the starting date for the stats. Defaults to 7 days ago. Must be a valid date in the format U. Example: 1581418800

end_date   string  optional    

The Unix timestamp of the ending date for the stats. Defaults to now. Must be a valid date in the format U. Example: 1581678000

Staff

All the Endpoints regarding Staff members of your franchise

Staff list

requires authentication

Retrieve a list of all the staff members available. Staff members can either be already assigned to a Studio, or they can be the ones created via API. The results are paginated

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/staff?per_page=10&sort_by=email&sort_direction=asc&site_id=12345" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/staff"
);

const params = {
    "per_page": "10",
    "sort_by": "email",
    "sort_direction": "asc",
    "site_id": "12345",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/staff';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'per_page' => '10',
            'sort_by' => 'email',
            'sort_direction' => 'asc',
            'site_id' => '12345',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "name": "Ms. Audra Crooks II",
            "email": "gulgowski.asia@example.com",
            "phone": null
        },
        {
            "name": "Jermaine Tillman",
            "email": "mya96@example.com",
            "phone": null
        }
    ]
}
 

Request      

GET api/franchise/v1/staff

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Query Parameters

per_page   number  optional    

The amount of staff to return. Max: 250. Default: 250. Must be at least 1. Must not be greater than 250. Example: 10

sort_by   string  optional    

Sorts the results by either email, name, or creation_date. Default: email. Example: email

Must be one of:
  • email
  • name
  • creation_date
sort_direction   string  optional    

Sorts the results asc for ascending order, desc for descending order. Default: asc. Example: asc

Must be one of:
  • asc
  • desc
site_id   string  optional    

Limit the search to a specific SiteID. Default: null. Example: 12345

Create a Staff

requires authentication

Create a new staff member

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/staff" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"email\": \"gbailey@example.net\",
    \"name\": \"miyvdljnikhwaykcmyuwpwlvqwr\",
    \"phone\": \"architecto\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/staff"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "gbailey@example.net",
    "name": "miyvdljnikhwaykcmyuwpwlvqwr",
    "phone": "architecto"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/staff';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'email' => 'gbailey@example.net',
            'name' => 'miyvdljnikhwaykcmyuwpwlvqwr',
            'phone' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "name": "Ms. Elisabeth Okuneva",
        "email": "idickens@example.org",
        "phone": null,
        "studios": []
    }
}
 

Request      

POST api/franchise/v1/staff

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

Body Parameters

email   string     

Must be a valid email address. Example: gbailey@example.net

name   string     

Must be at least 0 characters. Example: miyvdljnikhwaykcmyuwpwlvqwr

phone   string  optional    

Example: architecto

Get Staff detail

requires authentication

Shows the details of a specific Staff member, including all the Studios they are part of and their role

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/staff/foo@bar.com" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/staff/foo@bar.com"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/staff/foo@bar.com';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "name": "Morgan Hirthe",
        "email": "dare.emelie@example.com",
        "phone": null,
        "studios": []
    }
}
 

Request      

GET api/franchise/v1/staff/{email}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

email   string     

The email of the Staff member Example: foo@bar.com

Detach a Staff

requires authentication

Detach a staff member from every Studio

Example request:
curl --request DELETE \
    "https://loopspark.com/api/franchise/v1/staff/foo@bar.com" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/staff/foo@bar.com"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/staff/foo@bar.com';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "name": "Morgan Hirthe",
        "email": "imclaughlin@example.org",
        "phone": null,
        "studios": []
    }
}
 

Request      

DELETE api/franchise/v1/staff/{email}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

email   string     

The email of the Staff member Example: foo@bar.com

Add a Staff to a Studio

requires authentication

Add a Staff member to a specific Studio, with a defined role.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/staff" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"email\": \"gbailey@example.net\",
    \"role\": \"owner\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/staff"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "gbailey@example.net",
    "role": "owner"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/staff';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'email' => 'gbailey@example.net',
            'role' => 'owner',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "foobar",
        "name": "My Yoga Studio",
        "phone_number": "12225551234",
        "api_set": true,
        "website": "https://mystudioisgreat.com/landing",
        "timezone": "UTC",
        "start_date": "2020-09-30",
        "creation_date": "2018-02-22T13:00:00+00:00",
        "first_import_done": true,
        "outgoing_settings": {
            "email": {
                "signature": "et",
                "sender": "umonahan@hotmail.com",
                "sender_name": "Prof. Nels Quitzon"
            },
            "phone": {
                "sender_name": "16273854741",
                "phone_forward": "12125558888"
            },
            "address": "4800 Oak Grove Dr, Pasadena, CA 91109"
        },
        "social": {
            "facebook_url": null,
            "instagram_handle": null,
            "twitter_handle": null
        },
        "urls": {
            "list_packages_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=41",
            "list_contracts_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=40",
            "book_class_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=-7"
        },
        "variables": {
            "packages": [
                {
                    "package_1": {
                        "url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=41",
                        "price": "$10",
                        "name": "Great Package",
                        "discount_code": "CouponName",
                        "discount_price": "20 dollars"
                    }
                },
                {
                    "package_2": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_3": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_4": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_5": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                }
            ],
            "contracts": [
                {
                    "contract_1": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_2": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_3": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_4": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_5": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                }
            ]
        },
        "staff": [
            {
                "name": "Krystal Harvey",
                "email": "alexandrea87@example.com",
                "role": "owner"
            }
        ],
        "groups": [
            {
                "id": 1,
                "name": "front-desk",
                "staff": [
                    {
                        "name": "Katarina Funk",
                        "email": "john42@example.com",
                        "phone": null
                    }
                ]
            },
            {
                "id": 2,
                "name": "teachers",
                "staff": []
            }
        ]
    }
}
 

Request      

POST api/franchise/v1/{siteId}/staff

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

Body Parameters

email   string     

Must be a valid email address. Example: gbailey@example.net

role   string     

Example: owner

Must be one of:
  • member
  • owner
  • admin
  • manager

Change the Staff role

requires authentication

Change the role of a Staff member of a specific Studio. If the Staff member is not currently part of the Studio, it will be added to it.

Example request:
curl --request PUT \
    "https://loopspark.com/api/franchise/v1/21225/staff/foo@bar.com" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"role\": \"manager\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/staff/foo@bar.com"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "role": "manager"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/staff/foo@bar.com';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'role' => 'manager',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "foobar",
        "name": "My Yoga Studio",
        "phone_number": "12225551234",
        "api_set": true,
        "website": "https://mystudioisgreat.com/landing",
        "timezone": "UTC",
        "start_date": "2020-09-30",
        "creation_date": "2018-02-22T13:00:00+00:00",
        "first_import_done": true,
        "outgoing_settings": {
            "email": {
                "signature": "et",
                "sender": "umonahan@hotmail.com",
                "sender_name": "Prof. Nels Quitzon"
            },
            "phone": {
                "sender_name": "16273854741",
                "phone_forward": "12125558888"
            },
            "address": "4800 Oak Grove Dr, Pasadena, CA 91109"
        },
        "social": {
            "facebook_url": null,
            "instagram_handle": null,
            "twitter_handle": null
        },
        "urls": {
            "list_packages_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=41",
            "list_contracts_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=40",
            "book_class_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=-7"
        },
        "variables": {
            "packages": [
                {
                    "package_1": {
                        "url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=41",
                        "price": "$10",
                        "name": "Great Package",
                        "discount_code": "CouponName",
                        "discount_price": "20 dollars"
                    }
                },
                {
                    "package_2": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_3": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_4": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_5": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                }
            ],
            "contracts": [
                {
                    "contract_1": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_2": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_3": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_4": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_5": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                }
            ]
        },
        "staff": [
            {
                "name": "Krystal Harvey",
                "email": "alexandrea87@example.com",
                "role": "owner"
            }
        ],
        "groups": [
            {
                "id": 1,
                "name": "front-desk",
                "staff": [
                    {
                        "name": "Katarina Funk",
                        "email": "john42@example.com",
                        "phone": null
                    }
                ]
            },
            {
                "id": 2,
                "name": "teachers",
                "staff": []
            }
        ]
    }
}
 

Request      

PUT api/franchise/v1/{siteId}/staff/{email}

PATCH api/franchise/v1/{siteId}/staff/{email}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

email   string     

The email of the Staff member Example: foo@bar.com

Body Parameters

role   string     

Example: manager

Must be one of:
  • member
  • owner
  • admin
  • manager

Detach a Staff from a Studio

requires authentication

Removes a Staff member from a Studio

Example request:
curl --request DELETE \
    "https://loopspark.com/api/franchise/v1/21225/staff/foo@bar.com" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/staff/foo@bar.com"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/staff/foo@bar.com';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "foobar",
        "name": "My Yoga Studio",
        "phone_number": "12225551234",
        "api_set": true,
        "website": "https://mystudioisgreat.com/landing",
        "timezone": "UTC",
        "start_date": "2020-09-30",
        "creation_date": "2018-02-22T13:00:00+00:00",
        "first_import_done": true,
        "outgoing_settings": {
            "email": {
                "signature": "et",
                "sender": "umonahan@hotmail.com",
                "sender_name": "Prof. Nels Quitzon"
            },
            "phone": {
                "sender_name": "16273854741",
                "phone_forward": "12125558888"
            },
            "address": "4800 Oak Grove Dr, Pasadena, CA 91109"
        },
        "social": {
            "facebook_url": null,
            "instagram_handle": null,
            "twitter_handle": null
        },
        "urls": {
            "list_packages_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=41",
            "list_contracts_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=40",
            "book_class_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=-7"
        },
        "variables": {
            "packages": [
                {
                    "package_1": {
                        "url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=41",
                        "price": "$10",
                        "name": "Great Package",
                        "discount_code": "CouponName",
                        "discount_price": "20 dollars"
                    }
                },
                {
                    "package_2": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_3": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_4": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_5": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                }
            ],
            "contracts": [
                {
                    "contract_1": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_2": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_3": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_4": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_5": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                }
            ]
        },
        "staff": [
            {
                "name": "Krystal Harvey",
                "email": "alexandrea87@example.com",
                "role": "owner"
            }
        ],
        "groups": [
            {
                "id": 1,
                "name": "front-desk",
                "staff": [
                    {
                        "name": "Katarina Funk",
                        "email": "john42@example.com",
                        "phone": null
                    }
                ]
            },
            {
                "id": 2,
                "name": "teachers",
                "staff": []
            }
        ]
    }
}
 

Request      

DELETE api/franchise/v1/{siteId}/staff/{email}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID Example: 21225

email   string     

The email of the Staff member Example: foo@bar.com

Prospect

All the Endpoints regarding the prospects of a specific studio.

Prospect list

requires authentication

Retrieve a list of the prospects for a specific Studio. The results are paginated

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/123/prospects?per_page=10&page=1&sort_by=id&sort_direction=asc&email=&mobile_phone=&opted_out_emails=1&opted_out_sms=1&tags[]=signed-up" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/123/prospects"
);

const params = {
    "per_page": "10",
    "page": "1",
    "sort_by": "id",
    "sort_direction": "asc",
    "email": "",
    "mobile_phone": "",
    "opted_out_emails": "1",
    "opted_out_sms": "1",
    "tags[0]": "signed-up",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/123/prospects';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'per_page' => '10',
            'page' => '1',
            'sort_by' => 'id',
            'sort_direction' => 'asc',
            'email' => '',
            'mobile_phone' => '',
            'opted_out_emails' => '1',
            'opted_out_sms' => '1',
            'tags[0]' => 'signed-up',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "lead-rosario67@hotmail.com",
            "profile_url": "https://loopspark.com/go/client/117/lead-rosario67@hotmail.com",
            "email": "rosario67@hotmail.com",
            "first_name": "Kayden",
            "last_name": "Robel",
            "mobile_phone": "13572103900",
            "creation_date": "2021-02-03T11:32:02+00:00",
            "referred_by": null,
            "referrer": {
                "first_name": null,
                "last_name": null,
                "email": null,
                "phone": null,
                "location": null
            },
            "tags": [
                {
                    "name": "Member",
                    "assigned_at": "2025-01-15T09:30:00+00:00"
                }
            ],
            "custom_fields": [],
            "can_contact_by_email": true,
            "can_contact_by_sms": true
        },
        {
            "id": "lead-bryana.conn@hotmail.com",
            "profile_url": "https://loopspark.com/go/client/3553/lead-bryana.conn@hotmail.com",
            "email": "bryana.conn@hotmail.com",
            "first_name": "Grover",
            "last_name": "Oberbrunner",
            "mobile_phone": "13577302692",
            "creation_date": "1971-12-10T19:07:33+00:00",
            "referred_by": null,
            "referrer": {
                "first_name": null,
                "last_name": null,
                "email": null,
                "phone": null,
                "location": null
            },
            "tags": [
                {
                    "name": "Member",
                    "assigned_at": "2025-01-15T09:30:00+00:00"
                }
            ],
            "custom_fields": [],
            "can_contact_by_email": true,
            "can_contact_by_sms": true
        }
    ]
}
 

Request      

GET api/franchise/v1/{siteId}/prospects

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The ID of the Studio Example: 123

Query Parameters

per_page   number  optional    

The amount of prospects to return. Default: 250. Must be at least 1. Must not be greater than 250. Example: 10

page   number  optional    

The page number. Must be at least 1. Example: 1

sort_by   string  optional    

Sorts the results by either email or creation_date. Default: email. Example: id

Must be one of:
  • creation_date
  • email
sort_direction   string  optional    

Sorts the results asc for ascending order, desc for descending order. Default: asc. Example: asc

Must be one of:
  • asc
  • desc
email   string  optional    

Return only prospects with the selected email. Empty for all prospects . Must be a valid email address.

mobile_phone   string  optional    

Return only prospects with the selected phone number. Phone format is 12223334444.

opted_out_emails   boolean  optional    

true will return prospects opted out from email communications. false will show prospects that have not opted out. Do not pass the filter to show all. Example: true

opted_out_sms   boolean  optional    

true will return prospects opted out from SMS communications. false will show prospects that have not opted out. Do not pass the filter to show all. Example: true

tags   string[]  optional    

Filter prospects by assigned tag(s). Array.

Add a Prospect

requires authentication

Adds a Prospect to the specified Studio.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/prospects" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"email\": \"gbailey@example.net\",
    \"first_name\": \"miyvdljnikhwaykcmyuwpwlvqwr\",
    \"last_name\": \"sitcpscqldzsnrwtujwvlxjklqp\",
    \"referred_by\": \"architecto\",
    \"tags\": [
        \"ngzmiyvdljnikhwa\"
    ],
    \"referrer\": {
        \"first_name\": \"architecto\",
        \"last_name\": \"architecto\",
        \"email\": \"zbailey@example.net\",
        \"phone\": \"architecto\",
        \"location\": \"architecto\"
    }
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/prospects"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "gbailey@example.net",
    "first_name": "miyvdljnikhwaykcmyuwpwlvqwr",
    "last_name": "sitcpscqldzsnrwtujwvlxjklqp",
    "referred_by": "architecto",
    "tags": [
        "ngzmiyvdljnikhwa"
    ],
    "referrer": {
        "first_name": "architecto",
        "last_name": "architecto",
        "email": "zbailey@example.net",
        "phone": "architecto",
        "location": "architecto"
    }
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/prospects';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'email' => 'gbailey@example.net',
            'first_name' => 'miyvdljnikhwaykcmyuwpwlvqwr',
            'last_name' => 'sitcpscqldzsnrwtujwvlxjklqp',
            'referred_by' => 'architecto',
            'tags' => ['ngzmiyvdljnikhwa'],
            'referrer' => ['first_name' => 'architecto', 'last_name' => 'architecto', 'email' => 'zbailey@example.net', 'phone' => 'architecto', 'location' => 'architecto'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "data": {
        "id": "lead-info@loopspark.com",
        "email": "info@loopspark.com",
        "first_name": "Luca",
        "last_name": "Critelli",
        "mobile_phone": null,
        "creation_date": "2020-01-29T11:55:09+00:00",
        "tags": [
            {
                "name": "signed-up",
                "assigned_at": "2020-01-29T11:55:09+00:00"
            },
            {
                "name": "awesome-client",
                "assigned_at": "2020-01-29T11:55:09+00:00"
            }
        ]
    }
}
 

Example response (400):


{
    "error": true,
    "message": "The Prospect already exists"
}
 

Request      

POST api/franchise/v1/{siteId}/prospects

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

Body Parameters

email   string     

Must be a valid email address. Example: gbailey@example.net

mobile_phone   string  optional    
first_name   string  optional    

Must be at least 0 characters. Example: miyvdljnikhwaykcmyuwpwlvqwr

last_name   string  optional    

Must be at least 0 characters. Example: sitcpscqldzsnrwtujwvlxjklqp

referred_by   string  optional    

Example: architecto

tags   string[]  optional    

Must be at least 1 character.

referrer   object  optional    
first_name   string  optional    

Example: architecto

last_name   string  optional    

Example: architecto

email   string  optional    

Must be a valid email address. Example: zbailey@example.net

phone   string  optional    

Example: architecto

location   string  optional    

Example: architecto

Get a prospect

requires authentication

Returns the information about a specific prospect

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/21225/prospects/jack@bauer.com" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/prospects/jack@bauer.com"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/prospects/jack@bauer.com';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "lead-mhamill@marvin.net",
        "profile_url": "https://loopspark.com/go/client/8863/lead-mhamill@marvin.net",
        "email": "mhamill@marvin.net",
        "first_name": "Rhett",
        "last_name": "Kuhn",
        "mobile_phone": "13573900260",
        "creation_date": "1996-08-19T01:34:47+00:00",
        "referred_by": null,
        "referrer": {
            "first_name": null,
            "last_name": null,
            "email": null,
            "phone": null,
            "location": null
        },
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "can_contact_by_email": true,
        "can_contact_by_sms": true
    }
}
 

Request      

GET api/franchise/v1/{siteId}/prospects/{email}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID. Example: 21225

email   string     

The prospect email. Example: jack@bauer.com

Update a Prospect

requires authentication

Updates the information of a Prospect

Example request:
curl --request PUT \
    "https://loopspark.com/api/franchise/v1/21224/prospects/jack@bauer.com" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"subscribed_email\": false,
    \"subscribed_sms\": false,
    \"period_count\": 27,
    \"club_count\": 5,
    \"referrer\": {
        \"first_name\": \"John\",
        \"last_name\": \"Doe\",
        \"email\": \"john.doe@example.com\",
        \"phone\": \"555-123-4567\",
        \"location\": \"New York\"
    }
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21224/prospects/jack@bauer.com"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "subscribed_email": false,
    "subscribed_sms": false,
    "period_count": 27,
    "club_count": 5,
    "referrer": {
        "first_name": "John",
        "last_name": "Doe",
        "email": "john.doe@example.com",
        "phone": "555-123-4567",
        "location": "New York"
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21224/prospects/jack@bauer.com';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'subscribed_email' => false,
            'subscribed_sms' => false,
            'period_count' => 27,
            'club_count' => 5.0,
            'referrer' => ['first_name' => 'John', 'last_name' => 'Doe', 'email' => 'john.doe@example.com', 'phone' => '555-123-4567', 'location' => 'New York'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "lead-rosario67@hotmail.com",
        "profile_url": "https://loopspark.com/go/client/117/lead-rosario67@hotmail.com",
        "email": "rosario67@hotmail.com",
        "first_name": "Kayden",
        "last_name": "Robel",
        "mobile_phone": "13572103900",
        "creation_date": "2021-02-03T11:32:02+00:00",
        "referred_by": null,
        "referrer": {
            "first_name": null,
            "last_name": null,
            "email": null,
            "phone": null,
            "location": null
        },
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "can_contact_by_email": true,
        "can_contact_by_sms": true
    }
}
 

Example response (422, Trying to SMS opt in a prospect that manually opted out):


{
    "error": true,
    "message": "The prospect cannot be opted-in to SMS because they sent the opt-out text"
}
 

Request      

PUT api/franchise/v1/{siteId}/prospects/{email}

PATCH api/franchise/v1/{siteId}/prospects/{email}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21224

email   string     

The prospect email Example: jack@bauer.com

Body Parameters

subscribed_email   boolean  optional    

Unsubscribes an entity from email communications if false. Subscribes if true. Example: false

subscribed_sms   boolean  optional    

Unsubscribes an entity from SMS communications if false. Subscribes if true. Example: false

period_count   number  optional    

Manually set the period count for this entity. Must be at least 0. Example: 27

club_count   number  optional    

Manually set the club count for this entity (requires manual_club_count to be enabled for the studio). Must be at least 0. Example: 5

referrer   object  optional    

Object containing referrer information with optional properties: first_name, last_name, email, phone, location.

first_name   string  optional    

Example: John

last_name   string  optional    

Example: Doe

email   string  optional    

Must be a valid email address. Example: john.doe@example.com

phone   string  optional    

Example: 555-123-4567

location   string  optional    

Example: New York

Tags

Add a Tag

requires authentication

Assigns a Tag to a specific Prospect

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/prospects/test@email.com/tags" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"tag\": \"Member\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/prospects/test@email.com/tags"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag": "Member"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/prospects/test@email.com/tags';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'tag' => 'Member',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "lead-mhamill@marvin.net",
        "profile_url": "https://loopspark.com/go/client/8863/lead-mhamill@marvin.net",
        "email": "mhamill@marvin.net",
        "first_name": "Rhett",
        "last_name": "Kuhn",
        "mobile_phone": "13573900260",
        "creation_date": "1996-08-19T01:34:47+00:00",
        "referred_by": null,
        "referrer": {
            "first_name": null,
            "last_name": null,
            "email": null,
            "phone": null,
            "location": null
        },
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "can_contact_by_email": true,
        "can_contact_by_sms": true
    }
}
 

Request      

POST api/franchise/v1/{siteId}/prospects/{email}/tags

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

email   string     

The email of the Prospect Example: test@email.com

Body Parameters

tag   string     

The tag. Must be at least 1 character. Example: Member

Remove a Tag

requires authentication

Removes a Tag from a specific Prospect. If the Prospect does not have the Tag assigned, it will return an error 400

Example request:
curl --request DELETE \
    "https://loopspark.com/api/franchise/v1/21225/prospects/test@email.com/tags" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"tag\": \"signed-up-flow\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/prospects/test@email.com/tags"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag": "signed-up-flow"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/prospects/test@email.com/tags';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'tag' => 'signed-up-flow',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "lead-mhamill@marvin.net",
        "profile_url": "https://loopspark.com/go/client/8863/lead-mhamill@marvin.net",
        "email": "mhamill@marvin.net",
        "first_name": "Rhett",
        "last_name": "Kuhn",
        "mobile_phone": "13573900260",
        "creation_date": "1996-08-19T01:34:47+00:00",
        "referred_by": null,
        "referrer": {
            "first_name": null,
            "last_name": null,
            "email": null,
            "phone": null,
            "location": null
        },
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "can_contact_by_email": true,
        "can_contact_by_sms": true
    }
}
 

Example response (400):


{
    "error": true,
    "message": "The prospect does not have the tag signed-up"
}
 

Request      

DELETE api/franchise/v1/{siteId}/prospects/{email}/tags

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

email   string     

The email of the Prospect Example: test@email.com

Body Parameters

tag   string     

The tag. Must be at least 1 character. Example: signed-up-flow

Custom Fields

Get Prospect Custom Fields

requires authentication

Retrieve all custom field values for a specific prospect. Returns field definition along with current value.

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/100001/prospects/john.doe@example.com/custom-fields" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/100001/prospects/john.doe@example.com/custom-fields"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/100001/prospects/john.doe@example.com/custom-fields';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "EMERGENCY_CONTACT",
            "value": "Jane Doe - 555-1234"
        }
    ]
}
 

Request      

GET api/franchise/v1/{siteId}/prospects/{email}/custom-fields

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio's site identifier (e.g., MindBody site ID). Example: 100001

email   string     

The prospect's email address. Example: john.doe@example.com

Update Prospect Custom Fields

requires authentication

Batch update custom field values for a specific prospect. Can update multiple fields in a single request.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/100001/prospects/john.doe@example.com/custom-fields" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"fields\": [
        {
            \"id\": \"EMERGENCY_CONTACT\",
            \"value\": \"John Doe - 555-1234\"
        },
        {
            \"id\": \"THREE_CS\",
            \"value\": \"challenge\"
        }
    ]
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/100001/prospects/john.doe@example.com/custom-fields"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "fields": [
        {
            "id": "EMERGENCY_CONTACT",
            "value": "John Doe - 555-1234"
        },
        {
            "id": "THREE_CS",
            "value": "challenge"
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/100001/prospects/john.doe@example.com/custom-fields';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'fields' => [
                ['id' => 'EMERGENCY_CONTACT', 'value' => 'John Doe - 555-1234'],
                ['id' => 'THREE_CS', 'value' => 'challenge'],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "EMERGENCY_CONTACT",
            "value": "Jane Doe - 555-5678"
        }
    ]
}
 

Request      

POST api/franchise/v1/{siteId}/prospects/{email}/custom-fields

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio's site identifier (e.g., MindBody site ID). Example: 100001

email   string     

The prospect's email address. Example: john.doe@example.com

Body Parameters

fields   object[]     

Array of field updates. Must have at least 1 items.

*   object  optional    
id   string     

The custom field ID (e.g., "EMERGENCY_CONTACT") Example: architecto

value   string  optional    

nullable The field value Example: architecto

id   string     

The custom field ID/slug (e.g., "EMERGENCY_CONTACT", "THREE_CS"). Must match an existing stored value. Example: EMERGENCY_CONTACT

value   string  optional    

The value to set. Use null to clear the field. Type depends on field definition (string, boolean, date, etc.). Example: John Doe - 555-1234

Notes

Create a Note

requires authentication

Adds a note to a Prospect, found by email (not case-sensitive), dated now. The note is plain text: HTML is shown as text, and line breaks are kept. To categorize it, send the id of one of the studio's active note categories (see "List Note Categories"). Each call creates a new note, so do not retry a request that already succeeded. The note has no staff author; LoopSpark shows its author as "Integration".

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/prospects/jack@bauer.com/notes" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"note\": \"Called about the 6-week challenge.\",
    \"note_category_id\": \"9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/prospects/jack@bauer.com/notes"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "note": "Called about the 6-week challenge.",
    "note_category_id": "9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/prospects/jack@bauer.com/notes';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'note' => 'Called about the 6-week challenge.',
            'note_category_id' => '9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "data": {
        "id": "3f0d6a52-8a4e-4c43-9b1e-6f3c2a1d7e90",
        "note": "<p>Called about the 6-week challenge.</p>",
        "note_category_id": "9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10",
        "created_at": "2026-09-28T14:05:00+00:00"
    }
}
 

Example response (422):


{
    "message": "The note category is not available for this studio.",
    "errors": {
        "note_category_id": [
            "The note category is not available for this studio."
        ]
    }
}
 

Request      

POST api/franchise/v1/{siteId}/prospects/{email}/notes

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID. Example: 21225

email   string     

The prospect email. Not case-sensitive. Example: jack@bauer.com

Body Parameters

note   string     

The note text, at least 2 characters. Plain text; line breaks are kept. Example: Called about the 6-week challenge.

note_category_id   string  optional    

Optional. The id of one of the studio's active note categories. Example: 9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10

Messages

Send a Text Message

requires authentication

Sends one SMS to a Prospect, found by email (not case-sensitive), from the studio's texting number, in the same conversation thread staff use in LoopSpark. Placeholders are filled in as in LoopSpark: write the name in double curly braces, for example FIRST_NAME. The message can be at most 3 SMS segments long, counted before placeholders are filled in. Opt-outs (STOP) and the usual opt-out notice work exactly as for a staff send, and the text counts against the studio's SMS bundle. A 201 means the text was queued; delivery happens asynchronously. Each call sends a new text, so do not retry a request that already succeeded. The text has no staff sender; the LoopSpark web app shows its sender as "Integration". Returns 422 when the person is muted, has opted out of texts, or has no valid mobile number, or when the text is empty once placeholders are filled in. Returns 403 when the studio is suspended.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/prospects/jack@bauer.com/messages" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"message\": \"Hi there, see you at the 6pm class!\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/prospects/jack@bauer.com/messages"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "message": "Hi there, see you at the 6pm class!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/prospects/jack@bauer.com/messages';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'message' => 'Hi there, see you at the 6pm class!',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "data": {
        "id": "3f0d6a52-8a4e-4c43-9b1e-6f3c2a1d7e90",
        "discussion_id": "9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10",
        "created_at": "2026-10-02T14:05:00+00:00"
    }
}
 

Example response (422):


{
    "message": "This client cannot be contacted by text message",
    "errors": {
        "message": [
            "This client cannot be contacted by text message"
        ]
    }
}
 

Request      

POST api/franchise/v1/{siteId}/prospects/{email}/messages

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID. Example: 21225

email   string     

The prospect email. Not case-sensitive. Example: jack@bauer.com

Body Parameters

message   string     

The text to send, at most 3 SMS segments before placeholders are filled in. Placeholders (a name such as FIRST_NAME in double curly braces) are filled in. Example: Hi there, see you at the 6pm class!

Unsubscribe Rules

All the Endpoints regarding the unsubscribe rules of a specific studio. This feature allows you to tell LoopSpark that when we see a client or prospect with the specified UniqueID, LoopSpark should consider them (un)subscribed on SMS and/or Email. Prospects' UniqueID can be retrieved from the "Prospect list" endpoint, using the returned id of the prospect

Add (un)subscribe rule

requires authentication

Add one or multiple (un)subscribe rules for a specific Studio. If an Unsubscribe Rule is already present for the specified client ID, it will be updated.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/unsubscribe_clients" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"clients\": [
        {
            \"id\": \"1000017\",
            \"unsubscribe\": true,
            \"type\": \"email\"
        }
    ]
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/unsubscribe_clients"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "clients": [
        {
            "id": "1000017",
            "unsubscribe": true,
            "type": "email"
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/unsubscribe_clients';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'clients' => [
                ['id' => '1000017', 'unsubscribe' => true, 'type' => 'email'],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "client_id": "681",
            "unsubscribe": true,
            "type": "sms",
            "valid_since": "2026-10-03T08:00:36+00:00"
        },
        {
            "client_id": "369",
            "unsubscribe": true,
            "type": "sms",
            "valid_since": "2026-10-03T08:00:36+00:00"
        }
    ]
}
 

Request      

POST api/franchise/v1/{siteId}/unsubscribe_clients

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

Body Parameters

clients   object[]  optional    
id   string     

The MBO UniqueID of the client, or the ID of a Prospect. Example: 1000017

unsubscribe   boolean     

Whether or not the client is (or will be) unsubscribed from the specified type. Example: true

type   string     

Describes which type of (un)subscription this rule is about. Example: email

Must be one of:
  • sms
  • email

Remove unsubscribe rule

requires authentication

Removes any unsubscribe rule for a specific Client

Example request:
curl --request DELETE \
    "https://loopspark.com/api/franchise/v1/21225/unsubscribe_clients/10000012" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"type\": \"email\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/unsubscribe_clients/10000012"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "email"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/unsubscribe_clients/10000012';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'type' => 'email',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "error": false,
    "deleted": true,
    "success": true
}
 

Example response (400):


{
    "error": true,
    "message": "No rule was found with that unique id and type combination"
}
 

Request      

DELETE api/franchise/v1/{siteId}/unsubscribe_clients/{uniqueid}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

uniqueid   string     

The MBO UniqueID of the Client, or the ID of a Prospect Example: 10000012

Body Parameters

type   string     

Describes which type of (un)subscription to delete. Example: email

Must be one of:
  • sms
  • email

Groups

All the Endpoints regarding the groups and the Staff assigned to them.

List Groups of a Studio

requires authentication

List the Groups of a Studio and the respective Staff members

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/21225/groups" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/groups"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/groups';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": 1,
            "name": "foobar",
            "staff": [
                {
                    "name": "Dr. Zane Roberts Sr.",
                    "email": "foo@current.com",
                    "phone": null
                },
                {
                    "name": "Kendrick O'Kon",
                    "email": "stehr.ciara@example.com",
                    "phone": null
                }
            ]
        },
        {
            "id": 2,
            "name": "frontdesk",
            "staff": [
                {
                    "name": "Miss Megane Feeney",
                    "email": "myriam99@example.net",
                    "phone": null
                },
                {
                    "name": "Benedict Medhurst",
                    "email": "cassin.adrianna@example.net",
                    "phone": null
                },
                {
                    "name": "Roderick Lebsack",
                    "email": "gabriella.bogisich@example.net",
                    "phone": null
                },
                {
                    "name": "Okey Grady",
                    "email": "tpagac@example.org",
                    "phone": null
                }
            ]
        }
    ]
}
 

Request      

GET api/franchise/v1/{siteId}/groups

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID Example: 21225

Create a Group

requires authentication

Use this endpoint to create a Group. If the Group is already present, the passed Staff will be added to the Group.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/groups" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"group_name\": \"frontdesk\",
    \"staff\": [
        \"gbailey@example.net\"
    ]
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/groups"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "group_name": "frontdesk",
    "staff": [
        "gbailey@example.net"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/groups';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'group_name' => 'frontdesk',
            'staff' => ['gbailey@example.net'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201, Successfully created):


{
    "data": {
        "id": 14,
        "name": "frontdesk",
        "staff": [
            {
                "name": "Jane Doe",
                "email": "frontdesk@studio.com",
                "phone": null
            },
            {
                "name": "Jack Bauer",
                "email": "jack@bauer.com",
                "phone": null
            }
        ]
    }
}
 

Example response (422, Staff is not part of the Studio):


{
    "message": "The given data was invalid.",
    "errors": {
        "staff.1": [
            "Staff member does not exist"
        ]
    }
}
 

Request      

POST api/franchise/v1/{siteId}/groups

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

Body Parameters

group_name   string     

The name of the Group. Must be at least 3 characters. Must not be greater than 50 characters. Example: frontdesk

staff   string[]     

Must be a valid email address.

Update Group Members

requires authentication

Use this endpoint to update the Staff members assigned to a specific Group. This will detach any other Staff from the Group, and assign only the ones specified in the staff array

Example request:
curl --request PATCH \
    "https://loopspark.com/api/franchise/v1/21225/groups" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"group_name\": \"frontdesk\",
    \"staff\": [
        \"gbailey@example.net\"
    ]
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/groups"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "group_name": "frontdesk",
    "staff": [
        "gbailey@example.net"
    ]
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/groups';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'group_name' => 'frontdesk',
            'staff' => ['gbailey@example.net'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201, Successfully updated):


{
    "data": {
        "id": 14,
        "name": "frontdesk",
        "staff": [
            {
                "name": "Jane Doe",
                "email": "frontdesk@studio.com",
                "phone": null
            },
            {
                "name": "Jack Bauer",
                "email": "jack@bauer.com",
                "phone": null
            }
        ]
    }
}
 

Example response (422, Staff is not part of the Studio):


{
    "message": "The given data was invalid.",
    "errors": {
        "staff.1": [
            "Staff member does not exist"
        ]
    }
}
 

Request      

PATCH api/franchise/v1/{siteId}/groups

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

Body Parameters

group_name   string     

The name of the Group. Must be at least 3 characters. Must not be greater than 50 characters. Example: frontdesk

staff   string[]     

Must be a valid email address.

Studio

All the Endpoints regarding the studios belonging to your franchise.

Studio list

requires authentication

Retrieve the list of all the studios available. The results are paginated

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/studios?per_page=10&sort_by=id&sort_direction=asc" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/studios"
);

const params = {
    "per_page": "10",
    "sort_by": "id",
    "sort_direction": "asc",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/studios';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'per_page' => '10',
            'sort_by' => 'id',
            'sort_direction' => 'asc',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "739",
            "name": "Dr. Courtney D'Amore II",
            "api_set": true,
            "phone_number": null,
            "timezone": "UTC",
            "website": "http://www.mosciski.com/dolorem-expedita-vero-natus-laudantium-delectus-sunt-neque",
            "creation_date": "2026-10-03T08:00:35+00:00",
            "start_date": null,
            "first_import_done": true,
            "is_template": false,
            "outgoing_settings": {
                "email": {
                    "signature": "alias",
                    "sender": "xokuneva@eichmann.com",
                    "sender_name": "Aracely Yost"
                },
                "phone": {
                    "sender_name": "10912332112",
                    "phone_forward": null
                },
                "address": "78972 Floyd Row\nSouth Fayetown, OR 63567-4075"
            },
            "social": {
                "facebook_url": null,
                "instagram_handle": null,
                "twitter_handle": null
            },
            "urls": {
                "list_packages_url": "https://clients.mindbodyonline.com/classic/ws?studioid=739&stype=41",
                "list_contracts_url": "https://clients.mindbodyonline.com/classic/ws?studioid=739&stype=40",
                "book_class_url": "https://clients.mindbodyonline.com/classic/ws?studioid=739&stype=-7"
            },
            "variables": {
                "packages": [
                    {
                        "package_1": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "package_2": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "package_3": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "package_4": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "package_5": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    }
                ],
                "contracts": [
                    {
                        "contract_1": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "contract_2": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "contract_3": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "contract_4": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "contract_5": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    }
                ]
            },
            "period_count": {
                "type": "ALL_TIME",
                "day": 0
            },
            "manual_club_count": false
        },
        {
            "id": "5718",
            "name": "Violet Kub",
            "api_set": true,
            "phone_number": null,
            "timezone": "UTC",
            "website": "http://kozey.com/",
            "creation_date": "2026-10-03T08:00:35+00:00",
            "start_date": null,
            "first_import_done": true,
            "is_template": false,
            "outgoing_settings": {
                "email": {
                    "signature": "rerum",
                    "sender": "emarvin@yahoo.com",
                    "sender_name": "Gaetano Altenwerth"
                },
                "phone": {
                    "sender_name": "13506254432",
                    "phone_forward": null
                },
                "address": "37248 Wuckert View\nWest Francisco, NC 09772-5660"
            },
            "social": {
                "facebook_url": null,
                "instagram_handle": null,
                "twitter_handle": null
            },
            "urls": {
                "list_packages_url": "https://clients.mindbodyonline.com/classic/ws?studioid=5718&stype=41",
                "list_contracts_url": "https://clients.mindbodyonline.com/classic/ws?studioid=5718&stype=40",
                "book_class_url": "https://clients.mindbodyonline.com/classic/ws?studioid=5718&stype=-7"
            },
            "variables": {
                "packages": [
                    {
                        "package_1": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "package_2": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "package_3": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "package_4": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "package_5": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    }
                ],
                "contracts": [
                    {
                        "contract_1": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "contract_2": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "contract_3": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "contract_4": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    },
                    {
                        "contract_5": {
                            "url": null,
                            "price": null,
                            "name": null,
                            "discount_code": null,
                            "discount_price": null
                        }
                    }
                ]
            },
            "period_count": {
                "type": "ALL_TIME",
                "day": 0
            },
            "manual_club_count": false
        }
    ]
}
 

Request      

GET api/franchise/v1/studios

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Query Parameters

per_page   number  optional    

The amount of studios to return. Max: 250. Default: 250. Must be at least 1. Must not be greater than 250. Example: 10

sort_by   string  optional    

Sorts the results by either id or creation_date. Default: id. Example: id

Must be one of:
  • creation_date
  • id
sort_direction   string  optional    

Sorts the results asc for ascending order, desc for descending order. Default: asc. Example: asc

Must be one of:
  • asc
  • desc

Get a Studio

requires authentication

Shows the details of a specific studio. It shows also a list of the staff members assigned to this Studio and their respective role

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/studios/21225" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/studios/21225"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/studios/21225';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "foobar",
        "name": "My Yoga Studio",
        "phone_number": "12225551234",
        "api_set": true,
        "website": "https://mystudioisgreat.com/landing",
        "timezone": "UTC",
        "start_date": "2020-09-30",
        "creation_date": "2018-02-22T13:00:00+00:00",
        "first_import_done": true,
        "outgoing_settings": {
            "email": {
                "signature": "et",
                "sender": "umonahan@hotmail.com",
                "sender_name": "Prof. Nels Quitzon"
            },
            "phone": {
                "sender_name": "16273854741",
                "phone_forward": "12125558888"
            },
            "address": "4800 Oak Grove Dr, Pasadena, CA 91109"
        },
        "social": {
            "facebook_url": null,
            "instagram_handle": null,
            "twitter_handle": null
        },
        "urls": {
            "list_packages_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=41",
            "list_contracts_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=40",
            "book_class_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=-7"
        },
        "variables": {
            "packages": [
                {
                    "package_1": {
                        "url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=41",
                        "price": "$10",
                        "name": "Great Package",
                        "discount_code": "CouponName",
                        "discount_price": "20 dollars"
                    }
                },
                {
                    "package_2": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_3": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_4": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_5": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                }
            ],
            "contracts": [
                {
                    "contract_1": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_2": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_3": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_4": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_5": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                }
            ]
        },
        "staff": [
            {
                "name": "Krystal Harvey",
                "email": "alexandrea87@example.com",
                "role": "owner"
            }
        ],
        "groups": [
            {
                "id": 1,
                "name": "front-desk",
                "staff": [
                    {
                        "name": "Katarina Funk",
                        "email": "john42@example.com",
                        "phone": null
                    }
                ]
            },
            {
                "id": 2,
                "name": "teachers",
                "staff": []
            }
        ]
    }
}
 

Request      

GET api/franchise/v1/studios/{siteId}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID Example: 21225

Update a Studio

requires authentication

Update a specific studio information

Example request:
curl --request PUT \
    "https://loopspark.com/api/franchise/v1/studios/21225" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"name\": \"bngzmiyvdljnikhw\",
    \"timezone\": \"America\\/Indiana\\/Tell_City\",
    \"start_date\": \"2020-09-30\",
    \"email_signature\": \"architecto\",
    \"email_sender\": \"zbailey@example.net\",
    \"email_sender_name\": \"iyvdljnikhwaykcm\",
    \"sms_sender\": \"yuwpwlvqwrsitcps\",
    \"phone_forward\": \"15552221234\",
    \"facebook_url\": \"http:\\/\\/mclaughlin.info\\/dolores-enim-non-facere-tempora\",
    \"instagram_handle\": \"@loopspark\",
    \"twitter_handle\": \"@loopspark\",
    \"list_packages_url\": \"http:\\/\\/www.leffler.info\\/quis-adipisci-molestias-fugit-deleniti-distinctio-eum\",
    \"list_contracts_url\": \"https:\\/\\/nitzsche.net\\/aliquam-veniam-corporis-dolorem-mollitia-deleniti-nemo.html\",
    \"book_class_url\": \"https:\\/\\/www.balistreri.org\\/dignissimos-neque-blanditiis-odio\",
    \"can_create_automations\": true,
    \"manual_club_count\": true,
    \"website\": \"https:\\/\\/bradtke.org\\/fugit-qui-repudiandae-laboriosam-est-alias.html\",
    \"period_count_type\": \"API\",
    \"period_count_day\": 1,
    \"package_1_url\": \"http:\\/\\/howe.com\\/accusamus-ut-et-recusandae.html\",
    \"package_1_price\": \"architecto\",
    \"package_1_name\": \"architecto\",
    \"package_1_discount_code\": \"architecto\",
    \"package_1_discount_price\": \"architecto\",
    \"package_2_url\": \"http:\\/\\/bailey.com\\/\",
    \"package_2_price\": \"architecto\",
    \"package_2_name\": \"architecto\",
    \"package_2_discount_code\": \"architecto\",
    \"package_2_discount_price\": \"architecto\",
    \"package_3_url\": \"http:\\/\\/bailey.com\\/\",
    \"package_3_price\": \"architecto\",
    \"package_3_name\": \"architecto\",
    \"package_3_discount_code\": \"architecto\",
    \"package_3_discount_price\": \"architecto\",
    \"package_4_url\": \"http:\\/\\/bailey.com\\/\",
    \"package_4_price\": \"architecto\",
    \"package_4_name\": \"architecto\",
    \"package_4_discount_code\": \"architecto\",
    \"package_4_discount_price\": \"architecto\",
    \"package_5_url\": \"http:\\/\\/bailey.com\\/\",
    \"package_5_price\": \"architecto\",
    \"package_5_name\": \"architecto\",
    \"package_5_discount_code\": \"architecto\",
    \"package_5_discount_price\": \"architecto\",
    \"contract_1_url\": \"http:\\/\\/bailey.com\\/\",
    \"contract_1_price\": \"architecto\",
    \"contract_1_name\": \"architecto\",
    \"contract_1_discount_code\": \"architecto\",
    \"contract_1_discount_price\": \"architecto\",
    \"contract_2_url\": \"http:\\/\\/bailey.com\\/\",
    \"contract_2_price\": \"architecto\",
    \"contract_2_name\": \"architecto\",
    \"contract_2_discount_code\": \"architecto\",
    \"contract_2_discount_price\": \"architecto\",
    \"contract_3_url\": \"http:\\/\\/bailey.com\\/\",
    \"contract_3_price\": \"architecto\",
    \"contract_3_name\": \"architecto\",
    \"contract_3_discount_code\": \"architecto\",
    \"contract_3_discount_price\": \"architecto\",
    \"contract_4_url\": \"http:\\/\\/bailey.com\\/\",
    \"contract_4_price\": \"architecto\",
    \"contract_4_name\": \"architecto\",
    \"contract_4_discount_code\": \"architecto\",
    \"contract_4_discount_price\": \"architecto\",
    \"contract_5_url\": \"http:\\/\\/bailey.com\\/\",
    \"contract_5_price\": \"architecto\",
    \"contract_5_name\": \"architecto\",
    \"contract_5_discount_code\": \"architecto\",
    \"contract_5_discount_price\": \"architecto\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/studios/21225"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "bngzmiyvdljnikhw",
    "timezone": "America\/Indiana\/Tell_City",
    "start_date": "2020-09-30",
    "email_signature": "architecto",
    "email_sender": "zbailey@example.net",
    "email_sender_name": "iyvdljnikhwaykcm",
    "sms_sender": "yuwpwlvqwrsitcps",
    "phone_forward": "15552221234",
    "facebook_url": "http:\/\/mclaughlin.info\/dolores-enim-non-facere-tempora",
    "instagram_handle": "@loopspark",
    "twitter_handle": "@loopspark",
    "list_packages_url": "http:\/\/www.leffler.info\/quis-adipisci-molestias-fugit-deleniti-distinctio-eum",
    "list_contracts_url": "https:\/\/nitzsche.net\/aliquam-veniam-corporis-dolorem-mollitia-deleniti-nemo.html",
    "book_class_url": "https:\/\/www.balistreri.org\/dignissimos-neque-blanditiis-odio",
    "can_create_automations": true,
    "manual_club_count": true,
    "website": "https:\/\/bradtke.org\/fugit-qui-repudiandae-laboriosam-est-alias.html",
    "period_count_type": "API",
    "period_count_day": 1,
    "package_1_url": "http:\/\/howe.com\/accusamus-ut-et-recusandae.html",
    "package_1_price": "architecto",
    "package_1_name": "architecto",
    "package_1_discount_code": "architecto",
    "package_1_discount_price": "architecto",
    "package_2_url": "http:\/\/bailey.com\/",
    "package_2_price": "architecto",
    "package_2_name": "architecto",
    "package_2_discount_code": "architecto",
    "package_2_discount_price": "architecto",
    "package_3_url": "http:\/\/bailey.com\/",
    "package_3_price": "architecto",
    "package_3_name": "architecto",
    "package_3_discount_code": "architecto",
    "package_3_discount_price": "architecto",
    "package_4_url": "http:\/\/bailey.com\/",
    "package_4_price": "architecto",
    "package_4_name": "architecto",
    "package_4_discount_code": "architecto",
    "package_4_discount_price": "architecto",
    "package_5_url": "http:\/\/bailey.com\/",
    "package_5_price": "architecto",
    "package_5_name": "architecto",
    "package_5_discount_code": "architecto",
    "package_5_discount_price": "architecto",
    "contract_1_url": "http:\/\/bailey.com\/",
    "contract_1_price": "architecto",
    "contract_1_name": "architecto",
    "contract_1_discount_code": "architecto",
    "contract_1_discount_price": "architecto",
    "contract_2_url": "http:\/\/bailey.com\/",
    "contract_2_price": "architecto",
    "contract_2_name": "architecto",
    "contract_2_discount_code": "architecto",
    "contract_2_discount_price": "architecto",
    "contract_3_url": "http:\/\/bailey.com\/",
    "contract_3_price": "architecto",
    "contract_3_name": "architecto",
    "contract_3_discount_code": "architecto",
    "contract_3_discount_price": "architecto",
    "contract_4_url": "http:\/\/bailey.com\/",
    "contract_4_price": "architecto",
    "contract_4_name": "architecto",
    "contract_4_discount_code": "architecto",
    "contract_4_discount_price": "architecto",
    "contract_5_url": "http:\/\/bailey.com\/",
    "contract_5_price": "architecto",
    "contract_5_name": "architecto",
    "contract_5_discount_code": "architecto",
    "contract_5_discount_price": "architecto"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/studios/21225';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'name' => 'bngzmiyvdljnikhw',
            'timezone' => 'America/Indiana/Tell_City',
            'start_date' => '2020-09-30',
            'email_signature' => 'architecto',
            'email_sender' => 'zbailey@example.net',
            'email_sender_name' => 'iyvdljnikhwaykcm',
            'sms_sender' => 'yuwpwlvqwrsitcps',
            'phone_forward' => '15552221234',
            'facebook_url' => 'http://mclaughlin.info/dolores-enim-non-facere-tempora',
            'instagram_handle' => '@loopspark',
            'twitter_handle' => '@loopspark',
            'list_packages_url' => 'http://www.leffler.info/quis-adipisci-molestias-fugit-deleniti-distinctio-eum',
            'list_contracts_url' => 'https://nitzsche.net/aliquam-veniam-corporis-dolorem-mollitia-deleniti-nemo.html',
            'book_class_url' => 'https://www.balistreri.org/dignissimos-neque-blanditiis-odio',
            'can_create_automations' => true,
            'manual_club_count' => true,
            'website' => 'https://bradtke.org/fugit-qui-repudiandae-laboriosam-est-alias.html',
            'period_count_type' => 'API',
            'period_count_day' => 1.0,
            'package_1_url' => 'http://howe.com/accusamus-ut-et-recusandae.html',
            'package_1_price' => 'architecto',
            'package_1_name' => 'architecto',
            'package_1_discount_code' => 'architecto',
            'package_1_discount_price' => 'architecto',
            'package_2_url' => 'http://bailey.com/',
            'package_2_price' => 'architecto',
            'package_2_name' => 'architecto',
            'package_2_discount_code' => 'architecto',
            'package_2_discount_price' => 'architecto',
            'package_3_url' => 'http://bailey.com/',
            'package_3_price' => 'architecto',
            'package_3_name' => 'architecto',
            'package_3_discount_code' => 'architecto',
            'package_3_discount_price' => 'architecto',
            'package_4_url' => 'http://bailey.com/',
            'package_4_price' => 'architecto',
            'package_4_name' => 'architecto',
            'package_4_discount_code' => 'architecto',
            'package_4_discount_price' => 'architecto',
            'package_5_url' => 'http://bailey.com/',
            'package_5_price' => 'architecto',
            'package_5_name' => 'architecto',
            'package_5_discount_code' => 'architecto',
            'package_5_discount_price' => 'architecto',
            'contract_1_url' => 'http://bailey.com/',
            'contract_1_price' => 'architecto',
            'contract_1_name' => 'architecto',
            'contract_1_discount_code' => 'architecto',
            'contract_1_discount_price' => 'architecto',
            'contract_2_url' => 'http://bailey.com/',
            'contract_2_price' => 'architecto',
            'contract_2_name' => 'architecto',
            'contract_2_discount_code' => 'architecto',
            'contract_2_discount_price' => 'architecto',
            'contract_3_url' => 'http://bailey.com/',
            'contract_3_price' => 'architecto',
            'contract_3_name' => 'architecto',
            'contract_3_discount_code' => 'architecto',
            'contract_3_discount_price' => 'architecto',
            'contract_4_url' => 'http://bailey.com/',
            'contract_4_price' => 'architecto',
            'contract_4_name' => 'architecto',
            'contract_4_discount_code' => 'architecto',
            'contract_4_discount_price' => 'architecto',
            'contract_5_url' => 'http://bailey.com/',
            'contract_5_price' => 'architecto',
            'contract_5_name' => 'architecto',
            'contract_5_discount_code' => 'architecto',
            'contract_5_discount_price' => 'architecto',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "foobar",
        "name": "My Yoga Studio",
        "phone_number": "12225551234",
        "api_set": true,
        "website": "https://mystudioisgreat.com/landing",
        "timezone": "UTC",
        "start_date": "2020-09-30",
        "creation_date": "2018-02-22T13:00:00+00:00",
        "first_import_done": true,
        "outgoing_settings": {
            "email": {
                "signature": "et",
                "sender": "umonahan@hotmail.com",
                "sender_name": "Prof. Nels Quitzon"
            },
            "phone": {
                "sender_name": "16273854741",
                "phone_forward": "12125558888"
            },
            "address": "4800 Oak Grove Dr, Pasadena, CA 91109"
        },
        "social": {
            "facebook_url": null,
            "instagram_handle": null,
            "twitter_handle": null
        },
        "urls": {
            "list_packages_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=41",
            "list_contracts_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=40",
            "book_class_url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=-7"
        },
        "variables": {
            "packages": [
                {
                    "package_1": {
                        "url": "https://clients.mindbodyonline.com/classic/ws?studioid=foobar&stype=41",
                        "price": "$10",
                        "name": "Great Package",
                        "discount_code": "CouponName",
                        "discount_price": "20 dollars"
                    }
                },
                {
                    "package_2": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_3": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_4": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "package_5": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                }
            ],
            "contracts": [
                {
                    "contract_1": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_2": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_3": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_4": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                },
                {
                    "contract_5": {
                        "url": null,
                        "price": null,
                        "name": null,
                        "discount_code": null,
                        "discount_price": null
                    }
                }
            ]
        },
        "staff": [
            {
                "name": "Krystal Harvey",
                "email": "alexandrea87@example.com",
                "role": "owner"
            }
        ],
        "groups": [
            {
                "id": 1,
                "name": "front-desk",
                "staff": [
                    {
                        "name": "Katarina Funk",
                        "email": "john42@example.com",
                        "phone": null
                    }
                ]
            },
            {
                "id": 2,
                "name": "teachers",
                "staff": []
            }
        ]
    }
}
 

Request      

PUT api/franchise/v1/studios/{siteId}

PATCH api/franchise/v1/studios/{siteId}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

Body Parameters

name   string  optional    

Sets the value of name. Must be at least 1 character. Example: bngzmiyvdljnikhw

timezone   string  optional    

Sets the value of timezone. Valid timezones are available at https://www.php.net/manual/en/timezones.php. Must be a valid time zone, such as Africa/Accra. Example: America/Indiana/Tell_City

start_date   string  optional    

Internal date to define when the Studio will open. Used in filters. Must be a valid date. Example: 2020-09-30

email_signature   string  optional    

Sets the value of email_signature. Nullable. Example: architecto

email_sender   string  optional    

Sets the value of email_sender. Must be a valid email address. Example: zbailey@example.net

email_sender_name   string  optional    

Sets the value of email_sender_name. Nullable. Must be at least 1 character. Example: iyvdljnikhwaykcm

sms_sender   string  optional    

Sets the value of sms_sender. Must be at least 1 character. Example: yuwpwlvqwrsitcps

phone_forward   string  optional    

Sets the value of phone_forward. Valid phone numbers are using format 15552221234. Example: 15552221234

facebook_url   string  optional    

Sets the value of facebook_url. Nullable. Must be a valid URL. Example: http://mclaughlin.info/dolores-enim-non-facere-tempora

instagram_handle   string  optional    

Sets the value of instagram_handle, without the @ symbol. Nullable. Example: @loopspark

twitter_handle   string  optional    

Sets the value of twitter_handle, without the @ symbol. Nullable. Example: @loopspark

list_packages_url   string  optional    

Sets the value of list_packages_url. Must be a valid URL. Example: http://www.leffler.info/quis-adipisci-molestias-fugit-deleniti-distinctio-eum

list_contracts_url   string  optional    

Sets the value of list_contracts_url. Must be a valid URL. Example: https://nitzsche.net/aliquam-veniam-corporis-dolorem-mollitia-deleniti-nemo.html

book_class_url   string  optional    

Sets the value of book_class_url. Must be a valid URL. Example: https://www.balistreri.org/dignissimos-neque-blanditiis-odio

can_create_automations   boolean  optional    

Allow the Studio members to create automations based on their role. Example: true

manual_club_count   boolean  optional    

When set to true, allows manual updating of club_count for Contacts and Clients. Example: true

website   string  optional    

The website of the Studio. Nullable. Must be a valid URL. Example: https://bradtke.org/fugit-qui-repudiandae-laboriosam-est-alias.html

address   string  optional    

The address of the Studio. It will be used in the merge-tag {{STUDIO_ADDRESS}}. Nullable.

period_count_type   string  optional    

Sets the period count calculation type. Valid options are ALL_TIME, DAY_OF_YEAR, API. Example: API

Must be one of:
  • ALL_TIME
  • DAY_OF_YEAR
  • API
period_count_day   number  optional    

Days since January 1st for the calculation of period count when using DAY_OF_YEAR. January 1st is day 1. This field is required when period_count_type is DAY_OF_YEAR. Must be at least 1. Must not be greater than 365. Example: 1

package_1_url   string  optional    

Must be a valid URL. Example: http://howe.com/accusamus-ut-et-recusandae.html

package_1_price   string  optional    

Example: architecto

package_1_name   string  optional    

Example: architecto

package_1_discount_code   string  optional    

Example: architecto

package_1_discount_price   string  optional    

Example: architecto

package_2_url   string  optional    

Must be a valid URL. Example: http://bailey.com/

package_2_price   string  optional    

Example: architecto

package_2_name   string  optional    

Example: architecto

package_2_discount_code   string  optional    

Example: architecto

package_2_discount_price   string  optional    

Example: architecto

package_3_url   string  optional    

Must be a valid URL. Example: http://bailey.com/

package_3_price   string  optional    

Example: architecto

package_3_name   string  optional    

Example: architecto

package_3_discount_code   string  optional    

Example: architecto

package_3_discount_price   string  optional    

Example: architecto

package_4_url   string  optional    

Must be a valid URL. Example: http://bailey.com/

package_4_price   string  optional    

Example: architecto

package_4_name   string  optional    

Example: architecto

package_4_discount_code   string  optional    

Example: architecto

package_4_discount_price   string  optional    

Example: architecto

package_5_url   string  optional    

Must be a valid URL. Example: http://bailey.com/

package_5_price   string  optional    

Example: architecto

package_5_name   string  optional    

Example: architecto

package_5_discount_code   string  optional    

Example: architecto

package_5_discount_price   string  optional    

Example: architecto

contract_1_url   string  optional    

Must be a valid URL. Example: http://bailey.com/

contract_1_price   string  optional    

Example: architecto

contract_1_name   string  optional    

Example: architecto

contract_1_discount_code   string  optional    

Example: architecto

contract_1_discount_price   string  optional    

Example: architecto

contract_2_url   string  optional    

Must be a valid URL. Example: http://bailey.com/

contract_2_price   string  optional    

Example: architecto

contract_2_name   string  optional    

Example: architecto

contract_2_discount_code   string  optional    

Example: architecto

contract_2_discount_price   string  optional    

Example: architecto

contract_3_url   string  optional    

Must be a valid URL. Example: http://bailey.com/

contract_3_price   string  optional    

Example: architecto

contract_3_name   string  optional    

Example: architecto

contract_3_discount_code   string  optional    

Example: architecto

contract_3_discount_price   string  optional    

Example: architecto

contract_4_url   string  optional    

Must be a valid URL. Example: http://bailey.com/

contract_4_price   string  optional    

Example: architecto

contract_4_name   string  optional    

Example: architecto

contract_4_discount_code   string  optional    

Example: architecto

contract_4_discount_price   string  optional    

Example: architecto

contract_5_url   string  optional    

Must be a valid URL. Example: http://bailey.com/

contract_5_price   string  optional    

Example: architecto

contract_5_name   string  optional    

Example: architecto

contract_5_discount_code   string  optional    

Example: architecto

contract_5_discount_price   string  optional    

Example: architecto

Forms

All the Endpoints regarding the groups and the Staff assigned to them.

Forms list

requires authentication

Retrieve a list of the forms for a specific Studio. The results are paginated

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/21225/forms?per_page=10&sort_by=id&sort_direction=asc&category=&include_archived=" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/forms"
);

const params = {
    "per_page": "10",
    "sort_by": "id",
    "sort_direction": "asc",
    "category": "",
    "include_archived": "0",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/forms';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'per_page' => '10',
            'sort_by' => 'id',
            'sort_direction' => 'asc',
            'category' => '',
            'include_archived' => '0',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "8818a7b7-d58c-4d9a-806d-bd382bb7dd52",
            "name": "explicabo",
            "category": "non",
            "archived": false,
            "creates_pdf": false,
            "form_url": "https://lo.ls/IdC1wXm",
            "send_email_copy_to": null,
            "allows_new_clients": false,
            "thank_you_url": null
        },
        {
            "id": "e4bf9b25-8d13-46b2-a66f-aa26aeaf6fae",
            "name": "et",
            "category": "assumenda",
            "archived": false,
            "creates_pdf": false,
            "form_url": "https://lo.ls/MQa5UUA",
            "send_email_copy_to": null,
            "allows_new_clients": false,
            "thank_you_url": null
        }
    ]
}
 

Request      

GET api/franchise/v1/{siteId}/forms

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID Example: 21225

Query Parameters

per_page   number  optional    

The amount of forms to return. Max: 250. Default: 50. Must be at least 1. Must not be greater than 250. Example: 10

sort_by   string  optional    

Sorts the results by either id or creation_date. Default: id. Example: id

Must be one of:
  • creation_date
  • id
sort_direction   string  optional    

Sorts the results asc for ascending order, desc for descending order. Default: asc. Example: asc

Must be one of:
  • asc
  • desc
category   string  optional    

Return only forms of this specific category. Empty for all forms.

include_archived   boolean  optional    

Include archived forms in the list. Default: false. Example: false

Get a single form

requires authentication

Returns the information of a specific form

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/21225/forms/61f48cb0-9b59-4ac5-8a46-c4192a5e750b" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/forms/61f48cb0-9b59-4ac5-8a46-c4192a5e750b"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/forms/61f48cb0-9b59-4ac5-8a46-c4192a5e750b';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "c4dece0b-6ac9-4961-9dc2-51fa495788b6",
        "name": "aut",
        "category": "adipisci",
        "archived": false,
        "creates_pdf": false,
        "form_url": "https://lo.ls/paorvUv",
        "send_email_copy_to": null,
        "allows_new_clients": false,
        "thank_you_url": null
    }
}
 

Request      

GET api/franchise/v1/{siteId}/forms/{formId}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID Example: 21225

formId   string     

The form ID Example: 61f48cb0-9b59-4ac5-8a46-c4192a5e750b

Client

All the Endpoints regarding the clients of a specific studio.

Client list

requires authentication

Retrieve a list of clients for a specific Studio. The results are paginated

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/123/clients?per_page=10&page=1&sort_by=id&sort_direction=asc&email=&mobile_phone=&opted_out_emails=1&opted_out_sms=1&tags[]=signed-up" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/123/clients"
);

const params = {
    "per_page": "10",
    "page": "1",
    "sort_by": "id",
    "sort_direction": "asc",
    "email": "",
    "mobile_phone": "",
    "opted_out_emails": "1",
    "opted_out_sms": "1",
    "tags[0]": "signed-up",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/123/clients';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'per_page' => '10',
            'page' => '1',
            'sort_by' => 'id',
            'sort_direction' => 'asc',
            'email' => '',
            'mobile_phone' => '',
            'opted_out_emails' => '1',
            'opted_out_sms' => '1',
            'tags[0]' => 'signed-up',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "2137449171",
            "team_id": "6789",
            "profile_url": "https://loopspark.com/go/client/6789/2137449171",
            "email": "kconsidine@hotmail.com",
            "active": true,
            "first_name": "Pauline",
            "last_name": "Schultz",
            "mobile_phone": "13577773167",
            "ltv": 0,
            "phone_home": null,
            "phone_work": null,
            "referred_by": null,
            "address": {
                "city": "North Eileen",
                "address": "4706 Madisen Avenue Suite 636\nNew Josianetown, WV 49408-0159",
                "postal_code": "79150-6985",
                "state": "CA"
            },
            "emergency_contact": {
                "email": "mitchell.matilda@yahoo.com",
                "name": "Mina Bauch",
                "phone": "13573073143",
                "relationship": "wife"
            },
            "birthday": "1972-08-25T23:37:54+00:00",
            "creation_date": "2023-10-10T09:48:20+00:00",
            "refreshed_at": null,
            "tags": [
                {
                    "name": "Member",
                    "assigned_at": "2025-01-15T09:30:00+00:00"
                }
            ],
            "custom_fields": [],
            "can_contact_by_email": true,
            "can_contact_by_sms": true,
            "unsubscribed_email_at": null,
            "unsubscribed_sms_at": null,
            "unsubscribed_sms_text": false,
            "referrer": {
                "first_name": null,
                "last_name": null,
                "email": null,
                "phone": null,
                "location": null
            },
            "stats": {
                "period_count": 12,
                "club_count": 0,
                "open_discussions": 0,
                "open_todos": 0
            },
            "social": {
                "twitter": [],
                "linkedin": [],
                "facebook": [],
                "instagram": []
            }
        },
        {
            "id": "1796147067",
            "team_id": "5854",
            "profile_url": "https://loopspark.com/go/client/5854/1796147067",
            "email": "tristin.mohr@leffler.com",
            "active": true,
            "first_name": "Vella",
            "last_name": "Ziemann",
            "mobile_phone": "13577240472",
            "ltv": 0,
            "phone_home": null,
            "phone_work": null,
            "referred_by": null,
            "address": {
                "city": "Maraborough",
                "address": "753 Jakubowski Track\nLake Daronside, MT 65023-7717",
                "postal_code": "31500-3812",
                "state": "CA"
            },
            "emergency_contact": {
                "email": "noe.ankunding@hotmail.com",
                "name": "Prof. Kenna Ortiz",
                "phone": "13571889386",
                "relationship": "wife"
            },
            "birthday": "2005-10-28T06:49:30+00:00",
            "creation_date": "2024-05-25T01:27:05+00:00",
            "refreshed_at": null,
            "tags": [
                {
                    "name": "Member",
                    "assigned_at": "2025-01-15T09:30:00+00:00"
                }
            ],
            "custom_fields": [],
            "can_contact_by_email": true,
            "can_contact_by_sms": true,
            "unsubscribed_email_at": null,
            "unsubscribed_sms_at": null,
            "unsubscribed_sms_text": false,
            "referrer": {
                "first_name": null,
                "last_name": null,
                "email": null,
                "phone": null,
                "location": null
            },
            "stats": {
                "period_count": 12,
                "club_count": 0,
                "open_discussions": 0,
                "open_todos": 0
            },
            "social": {
                "twitter": [],
                "linkedin": [],
                "facebook": [],
                "instagram": []
            }
        }
    ]
}
 

Request      

GET api/franchise/v1/{siteId}/clients

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The ID of the Studio Example: 123

Query Parameters

per_page   number  optional    

The amount of clients to return. Default: 250. Must be at least 1. Must not be greater than 250. Example: 10

page   number  optional    

The page number. Must be at least 1. Example: 1

sort_by   string  optional    

Sorts the results by either id or creation_date. Default: id. Example: id

Must be one of:
  • creation_date
  • id
sort_direction   string  optional    

Sorts the results asc for ascending order, desc for descending order. Default: asc. Example: asc

Must be one of:
  • asc
  • desc
email   string  optional    

Return only clients with the selected email. Empty for all clients . Must be a valid email address.

mobile_phone   string  optional    

Return only clients with the selected phone number. Phone format is 12223334444.

opted_out_emails   boolean  optional    

true will return clients opted out from email communications. false will show clients that have not opted out. Do not pass the filter to show all. Example: true

opted_out_sms   boolean  optional    

true will return clients opted out from SMS communications. false will show clients that have not opted out. Do not pass the filter to show all. Example: true

tags   string[]  optional    

Filter clients by assigned tag(s). Array.

Get a client

requires authentication

Returns the information about a specific client

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/21225/clients/10000023" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/clients/10000023"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/clients/10000023';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "4274032",
        "team_id": "2603",
        "profile_url": "https://loopspark.com/go/client/2603/4274032",
        "email": "green.doris@gmail.com",
        "active": true,
        "first_name": "Emanuel",
        "last_name": "Emmerich",
        "mobile_phone": "13573996721",
        "ltv": 0,
        "phone_home": null,
        "phone_work": null,
        "referred_by": null,
        "address": {
            "city": "New Monaland",
            "address": "422 Moen Corners\nSouth Moises, NH 64519",
            "postal_code": "91956",
            "state": "CA"
        },
        "emergency_contact": {
            "email": "kirsten.ward@yahoo.com",
            "name": "Hank Batz",
            "phone": "13570216494",
            "relationship": "wife"
        },
        "birthday": "1990-07-16T02:01:30+00:00",
        "creation_date": "2024-11-19T16:36:21+00:00",
        "refreshed_at": null,
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "can_contact_by_email": true,
        "can_contact_by_sms": true,
        "unsubscribed_email_at": null,
        "unsubscribed_sms_at": null,
        "unsubscribed_sms_text": false,
        "referrer": {
            "first_name": null,
            "last_name": null,
            "email": null,
            "phone": null,
            "location": null
        },
        "stats": {
            "period_count": 12,
            "club_count": 0,
            "open_discussions": 0,
            "open_todos": 0
        },
        "social": {
            "twitter": [],
            "linkedin": [],
            "facebook": [],
            "instagram": []
        }
    }
}
 

Request      

GET api/franchise/v1/{siteId}/clients/{clientId}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID. Example: 21225

clientId   string     

The client ID. Example: 10000023

Update a Client

requires authentication

Updates the information of a Client

Example request:
curl --request PUT \
    "https://loopspark.com/api/franchise/v1/21224/clients/10000024" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"subscribed_email\": false,
    \"subscribed_sms\": false,
    \"period_count\": 27,
    \"club_count\": 5,
    \"referrer\": {
        \"first_name\": \"John\",
        \"last_name\": \"Doe\",
        \"email\": \"john.doe@example.com\",
        \"phone\": \"555-123-4567\",
        \"location\": \"New York\"
    }
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21224/clients/10000024"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "subscribed_email": false,
    "subscribed_sms": false,
    "period_count": 27,
    "club_count": 5,
    "referrer": {
        "first_name": "John",
        "last_name": "Doe",
        "email": "john.doe@example.com",
        "phone": "555-123-4567",
        "location": "New York"
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21224/clients/10000024';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'subscribed_email' => false,
            'subscribed_sms' => false,
            'period_count' => 27,
            'club_count' => 5.0,
            'referrer' => ['first_name' => 'John', 'last_name' => 'Doe', 'email' => 'john.doe@example.com', 'phone' => '555-123-4567', 'location' => 'New York'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "524453158",
        "team_id": "9290",
        "profile_url": "https://loopspark.com/go/client/9290/524453158",
        "email": "qankunding@gmail.com",
        "active": true,
        "first_name": "Elton",
        "last_name": "Gaylord",
        "mobile_phone": "13576738443",
        "ltv": 0,
        "phone_home": null,
        "phone_work": null,
        "referred_by": null,
        "address": {
            "city": "South Dorisfurt",
            "address": "5805 Noah Via Suite 679\nSouth Katlyn, VA 78637",
            "postal_code": "80568",
            "state": "CA"
        },
        "emergency_contact": {
            "email": "vraynor@labadie.com",
            "name": "Mr. Carey Smitham",
            "phone": "13573341494",
            "relationship": "wife"
        },
        "birthday": "1986-10-21T08:44:51+00:00",
        "creation_date": "2026-08-12T05:46:43+00:00",
        "refreshed_at": null,
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "can_contact_by_email": false,
        "can_contact_by_sms": false,
        "unsubscribed_email_at": "2026-10-03T08:00:35+00:00",
        "unsubscribed_sms_at": "2026-10-03T08:00:35+00:00",
        "unsubscribed_sms_text": false,
        "referrer": {
            "first_name": null,
            "last_name": null,
            "email": null,
            "phone": null,
            "location": null
        },
        "stats": {
            "period_count": 12,
            "club_count": 0,
            "open_discussions": 0,
            "open_todos": 0
        },
        "social": {
            "twitter": [],
            "linkedin": [],
            "facebook": [],
            "instagram": []
        }
    }
}
 

Example response (403, Trying to update club_count when manual_club_count is disabled):


{
    "error": true,
    "message": "Cannot update club_count when manual_club_count is disabled for the studio"
}
 

Example response (422, Trying to SMS opt in a client that manually opted out):


{
    "error": true,
    "message": "The client cannot be opted-in to SMS because they sent the opt-out text"
}
 

Request      

PUT api/franchise/v1/{siteId}/clients/{clientId}

PATCH api/franchise/v1/{siteId}/clients/{clientId}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21224

clientId   string     

The client ID Example: 10000024

Body Parameters

subscribed_email   boolean  optional    

Unsubscribes an entity from email communications if false. Subscribes if true. Example: false

subscribed_sms   boolean  optional    

Unsubscribes an entity from SMS communications if false. Subscribes if true. Example: false

period_count   number  optional    

Manually set the period count for this entity. Must be at least 0. Example: 27

club_count   number  optional    

Manually set the club count for this entity (requires manual_club_count to be enabled for the studio). Must be at least 0. Example: 5

referrer   object  optional    

Object containing referrer information with optional properties: first_name, last_name, email, phone, location.

first_name   string  optional    

Example: John

last_name   string  optional    

Example: Doe

email   string  optional    

Must be a valid email address. Example: john.doe@example.com

phone   string  optional    

Example: 555-123-4567

location   string  optional    

Example: New York

Tags

Add a Tag

requires authentication

Assigns a Tag to a specific Client

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/clients/10000023/tags" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"tag\": \"Member\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/clients/10000023/tags"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag": "Member"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/clients/10000023/tags';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'tag' => 'Member',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "1365105717",
        "team_id": "9290",
        "profile_url": "https://loopspark.com/go/client/9290/1365105717",
        "email": "wbatz@kutch.com",
        "active": true,
        "first_name": "Micaela",
        "last_name": "Ankunding",
        "mobile_phone": "13576738443",
        "ltv": 0,
        "phone_home": null,
        "phone_work": null,
        "referred_by": null,
        "address": {
            "city": "South Dorisfurt",
            "address": "2669 Wolff Trail\nBeierburgh, VA 78637",
            "postal_code": "80568",
            "state": "CA"
        },
        "emergency_contact": {
            "email": "kconsidine@hotmail.com",
            "name": "Henri Mitchell",
            "phone": "13574946036",
            "relationship": "wife"
        },
        "birthday": "1986-10-21T08:44:51+00:00",
        "creation_date": "2026-08-12T05:46:43+00:00",
        "refreshed_at": null,
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "can_contact_by_email": true,
        "can_contact_by_sms": true,
        "unsubscribed_email_at": null,
        "unsubscribed_sms_at": null,
        "unsubscribed_sms_text": false,
        "referrer": {
            "first_name": null,
            "last_name": null,
            "email": null,
            "phone": null,
            "location": null
        },
        "stats": {
            "period_count": 12,
            "club_count": 0,
            "open_discussions": 0,
            "open_todos": 0
        },
        "social": {
            "twitter": [],
            "linkedin": [],
            "facebook": [],
            "instagram": []
        }
    }
}
 

Request      

POST api/franchise/v1/{siteId}/clients/{clientId}/tags

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

clientId   string     

The client ID Example: 10000023

Body Parameters

tag   string     

The tag. Must be at least 1 character. Example: Member

Remove a Tag

requires authentication

Removes a Tag from a specific Client. If the Client does not have the Tag assigned, it will return an error 400

Example request:
curl --request DELETE \
    "https://loopspark.com/api/franchise/v1/21225/clients/10000023/tags" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"tag\": \"signed-up-flow\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/clients/10000023/tags"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag": "signed-up-flow"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/clients/10000023/tags';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'tag' => 'signed-up-flow',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "1880026316",
        "team_id": "889",
        "profile_url": "https://loopspark.com/go/client/889/1880026316",
        "email": "torp.florence@wolff.info",
        "active": true,
        "first_name": "Cynthia",
        "last_name": "Fahey",
        "mobile_phone": "13577037707",
        "ltv": 0,
        "phone_home": null,
        "phone_work": null,
        "referred_by": null,
        "address": {
            "city": "New Modesta",
            "address": "85188 Victoria Shoals\nWest Artborough, NV 18607-5439",
            "postal_code": "26085",
            "state": "CA"
        },
        "emergency_contact": {
            "email": "mcdermott.verlie@gmail.com",
            "name": "Shayna Raynor Jr.",
            "phone": "13573786691",
            "relationship": "wife"
        },
        "birthday": "1966-10-14T12:39:49+00:00",
        "creation_date": "2025-02-26T10:16:56+00:00",
        "refreshed_at": null,
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "can_contact_by_email": true,
        "can_contact_by_sms": true,
        "unsubscribed_email_at": null,
        "unsubscribed_sms_at": null,
        "unsubscribed_sms_text": false,
        "referrer": {
            "first_name": null,
            "last_name": null,
            "email": null,
            "phone": null,
            "location": null
        },
        "stats": {
            "period_count": null,
            "club_count": 0,
            "open_discussions": 0,
            "open_todos": 0
        },
        "social": {
            "twitter": [],
            "linkedin": [],
            "facebook": [],
            "instagram": []
        }
    }
}
 

Example response (400):


{
    "error": true,
    "message": "The client does not have the tag signed-up"
}
 

Request      

DELETE api/franchise/v1/{siteId}/clients/{clientId}/tags

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

clientId   string     

The client ID Example: 10000023

Body Parameters

tag   string     

The tag. Must be at least 1 character. Example: signed-up-flow

Forms

Lists the form submissions

requires authentication

Lists the form submissions of a specific client

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/21225/clients/10000023/form_submissions?page=1&per_page=10&sort_by=creation_date&sort_direction=asc&form_id=&category=" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/clients/10000023/form_submissions"
);

const params = {
    "page": "1",
    "per_page": "10",
    "sort_by": "creation_date",
    "sort_direction": "asc",
    "form_id": "",
    "category": "",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/clients/10000023/form_submissions';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'page' => '1',
            'per_page' => '10',
            'sort_by' => 'creation_date',
            'sort_direction' => 'asc',
            'form_id' => '',
            'category' => '',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "a621f6a5-219b-4a92-bc68-04a8b423d629",
        "submission_date": "2026-10-03T08:00:35+00:00",
        "pdf_url": "http://www.bailey.biz/quos-velit-et-fugiat-sunt-nihil-accusantium-harum.html",
        "form": {
            "id": "d0ae0146-ed1a-47b8-9d38-d39cf6e0b512",
            "name": "rem",
            "category": "ea",
            "archived": false,
            "creates_pdf": false,
            "form_url": "https://lo.ls/C99cQnt",
            "send_email_copy_to": null,
            "allows_new_clients": false,
            "thank_you_url": null
        }
    }
}
 

Request      

GET api/franchise/v1/{siteId}/clients/{clientId}/form_submissions

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID. Example: 21225

clientId   string     

The client ID. Example: 10000023

Query Parameters

page   integer  optional    

The page number. Example: 1

per_page   number  optional    

The amount of clients to return. Max: 250. Default: 10. Must be at least 1. Must not be greater than 250. Example: 10

sort_by   string  optional    

Sorts the results by either id or creation_date. Default: creation_date. Example: creation_date

Must be one of:
  • creation_date
  • id
sort_direction   string  optional    

Sorts the results asc for ascending order, desc for descending order. Default: desc. Example: asc

Must be one of:
  • asc
  • desc
form_id   string  optional    

Return only forms submissions of a specific form. Must be a valid UUID. Must match an existing stored value.

category   string  optional    

Return only forms submissions of forms belonging to a specific category.

Custom Fields

Get Client Custom Fields

requires authentication

Retrieve all custom field values for a specific client. Returns field definition along with current value.

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/100001/clients/100000001/custom-fields" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/100001/clients/100000001/custom-fields"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/100001/clients/100000001/custom-fields';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "custom_field": {
                "id": "EMERGENCY_CONTACT",
                "name": "Emergency Contact",
                "type": "string",
                "field_options": null,
                "description": "Person to contact in case of emergency"
            },
            "value": "John Doe - 555-1234"
        },
        {
            "custom_field": {
                "id": "THREE_CS",
                "name": "Three Cs",
                "type": "single_select",
                "field_options": [
                    {
                        "value": "challenge",
                        "label": "Challenge",
                        "is_active": true,
                        "sort_order": 1
                    },
                    {
                        "value": "commitment",
                        "label": "Commitment",
                        "is_active": true,
                        "sort_order": 2
                    },
                    {
                        "value": "community",
                        "label": "Community",
                        "is_active": true,
                        "sort_order": 3
                    }
                ],
                "description": "Select primary motivation"
            },
            "value": "challenge"
        }
    ]
}
 

Request      

GET api/franchise/v1/{siteId}/clients/{clientId}/custom-fields

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio's site identifier (e.g., MindBody site ID). Example: 100001

clientId   string     

The client's external identifier (e.g., MindBody client ID). Example: 100000001

Update Client Custom Fields

requires authentication

Batch update custom field values for a specific client. Can update multiple fields in a single request.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/100001/clients/100000001/custom-fields" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"fields\": [
        {
            \"id\": \"EMERGENCY_CONTACT\",
            \"value\": \"John Doe - 555-1234\"
        },
        {
            \"id\": \"THREE_CS\",
            \"value\": \"challenge\"
        }
    ]
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/100001/clients/100000001/custom-fields"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "fields": [
        {
            "id": "EMERGENCY_CONTACT",
            "value": "John Doe - 555-1234"
        },
        {
            "id": "THREE_CS",
            "value": "challenge"
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/100001/clients/100000001/custom-fields';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'fields' => [
                ['id' => 'EMERGENCY_CONTACT', 'value' => 'John Doe - 555-1234'],
                ['id' => 'THREE_CS', 'value' => 'challenge'],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "custom_field": {
                "id": "EMERGENCY_CONTACT",
                "name": "Emergency Contact",
                "type": "string"
            },
            "value": "Jane Doe - 555-5678"
        }
    ]
}
 

Request      

POST api/franchise/v1/{siteId}/clients/{clientId}/custom-fields

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio's site identifier (e.g., MindBody site ID). Example: 100001

clientId   string     

The client's external identifier (e.g., MindBody client ID). Example: 100000001

Body Parameters

fields   object[]     

Array of field updates. Must have at least 1 items.

*   object  optional    
id   string     

The custom field ID (e.g., "EMERGENCY_CONTACT") Example: architecto

value   string  optional    

nullable The field value Example: architecto

id   string     

The custom field ID/slug (e.g., "EMERGENCY_CONTACT", "THREE_CS"). Must match an existing stored value. Example: EMERGENCY_CONTACT

value   string  optional    

The value to set. Use null to clear the field. Type depends on field definition (string, boolean, date, etc.). Example: John Doe - 555-1234

Notes

Create a Note

requires authentication

Adds a note to a Client, dated now. The note is plain text: HTML is shown as text, and line breaks are kept. To categorize it, send the id of one of the studio's active note categories (see "List Note Categories"). Each call creates a new note, so do not retry a request that already succeeded. The note has no staff author; LoopSpark shows its author as "Integration".

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/clients/10000023/notes" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"note\": \"Called about the 6-week challenge.\",
    \"note_category_id\": \"9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/clients/10000023/notes"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "note": "Called about the 6-week challenge.",
    "note_category_id": "9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/clients/10000023/notes';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'note' => 'Called about the 6-week challenge.',
            'note_category_id' => '9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "data": {
        "id": "3f0d6a52-8a4e-4c43-9b1e-6f3c2a1d7e90",
        "note": "<p>Called about the 6-week challenge.</p>",
        "note_category_id": "9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10",
        "created_at": "2026-09-28T14:05:00+00:00"
    }
}
 

Example response (422):


{
    "message": "The note category is not available for this studio.",
    "errors": {
        "note_category_id": [
            "The note category is not available for this studio."
        ]
    }
}
 

Request      

POST api/franchise/v1/{siteId}/clients/{clientId}/notes

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID. Example: 21225

clientId   string     

The client ID. Example: 10000023

Body Parameters

note   string     

The note text, at least 2 characters. Plain text; line breaks are kept. Example: Called about the 6-week challenge.

note_category_id   string  optional    

Optional. The id of one of the studio's active note categories. Example: 9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10

Messages

Send a Text Message

requires authentication

Sends one SMS to a Client from the studio's texting number, in the same conversation thread staff use in LoopSpark. Placeholders are filled in as in LoopSpark: write the name in double curly braces, for example FIRST_NAME. The message can be at most 3 SMS segments long, counted before placeholders are filled in. Opt-outs (STOP) and the usual opt-out notice work exactly as for a staff send, and the text counts against the studio's SMS bundle. A 201 means the text was queued; delivery happens asynchronously. Each call sends a new text, so do not retry a request that already succeeded. The text has no staff sender; the LoopSpark web app shows its sender as "Integration". Returns 422 when the person is muted, has opted out of texts, or has no valid mobile number, or when the text is empty once placeholders are filled in. Returns 403 when the studio is suspended.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/clients/10000023/messages" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"message\": \"Hi there, see you at the 6pm class!\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/clients/10000023/messages"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "message": "Hi there, see you at the 6pm class!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/clients/10000023/messages';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'message' => 'Hi there, see you at the 6pm class!',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "data": {
        "id": "3f0d6a52-8a4e-4c43-9b1e-6f3c2a1d7e90",
        "discussion_id": "9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10",
        "created_at": "2026-10-02T14:05:00+00:00"
    }
}
 

Example response (422):


{
    "message": "This client cannot be contacted by text message",
    "errors": {
        "message": [
            "This client cannot be contacted by text message"
        ]
    }
}
 

Request      

POST api/franchise/v1/{siteId}/clients/{clientId}/messages

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID. Example: 21225

clientId   string     

The client ID. Example: 10000023

Body Parameters

message   string     

The text to send, at most 3 SMS segments before placeholders are filled in. Placeholders (a name such as FIRST_NAME in double curly braces) are filled in. Example: Hi there, see you at the 6pm class!

Contacts

Endpoints related to managing studio contacts, encompassing both clients and prospects. A contact is a unified entity designed to facilitate seamless viewing and management of both clients and prospects within a single list. This makes it easier to find clients or prospects in the system with just one call.

Contacts list

requires authentication

Retrieve a list of all the contacts for a specific Studio. The results are paginated

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/123/contacts?per_page=10&page=1&sort_by=id&sort_direction=asc&email=&mobile_phone=&opted_out_emails=1&opted_out_sms=1&tags[]=signed-up" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/123/contacts"
);

const params = {
    "per_page": "10",
    "page": "1",
    "sort_by": "id",
    "sort_direction": "asc",
    "email": "",
    "mobile_phone": "",
    "opted_out_emails": "1",
    "opted_out_sms": "1",
    "tags[0]": "signed-up",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/123/contacts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'per_page' => '10',
            'page' => '1',
            'sort_by' => 'id',
            'sort_direction' => 'asc',
            'email' => '',
            'mobile_phone' => '',
            'opted_out_emails' => '1',
            'opted_out_sms' => '1',
            'tags[0]' => 'signed-up',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "lead-mhamill@marvin.net",
            "team_id": "8863",
            "profile_url": "https://loopspark.com/go/client/8863/lead-mhamill@marvin.net",
            "email": "mhamill@marvin.net",
            "active": false,
            "first_name": "Rhett",
            "last_name": "Kuhn",
            "mobile_phone": "13573900260",
            "can_contact_by_email": true,
            "can_contact_by_sms": true,
            "unsubscribed_email_at": null,
            "unsubscribed_sms_at": null,
            "is_prospect": true,
            "created_at": "2026-10-03T08:00:36+00:00",
            "tags": [
                {
                    "name": "Member",
                    "assigned_at": "2025-01-15T09:30:00+00:00"
                }
            ],
            "custom_fields": [],
            "client": null
        },
        {
            "id": "lead-conn.brisa@yahoo.com",
            "team_id": "751",
            "profile_url": "https://loopspark.com/go/client/751/lead-conn.brisa@yahoo.com",
            "email": "conn.brisa@yahoo.com",
            "active": false,
            "first_name": "Ethelyn",
            "last_name": "Lesch",
            "mobile_phone": "13570269273",
            "can_contact_by_email": true,
            "can_contact_by_sms": true,
            "unsubscribed_email_at": null,
            "unsubscribed_sms_at": null,
            "is_prospect": true,
            "created_at": "2026-10-03T08:00:36+00:00",
            "tags": [
                {
                    "name": "Member",
                    "assigned_at": "2025-01-15T09:30:00+00:00"
                }
            ],
            "custom_fields": [],
            "client": null
        }
    ]
}
 

Request      

GET api/franchise/v1/{siteId}/contacts

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The ID of the Studio Example: 123

Query Parameters

per_page   number  optional    

The amount of contacts to return. Default: 250. Must be at least 1. Must not be greater than 250. Example: 10

page   number  optional    

The page number. Must be at least 1. Example: 1

sort_by   string  optional    

Sorts the results by either id or creation_date. Default: id. Example: id

Must be one of:
  • creation_date
  • id
sort_direction   string  optional    

Sorts the results asc for ascending order, desc for descending order. Default: asc. Example: asc

Must be one of:
  • asc
  • desc
email   string  optional    

Return only contacts with the selected email. Empty for all contacts . Must be a valid email address.

mobile_phone   string  optional    

Return only contacts with the selected phone number. Phone format is 12223334444.

opted_out_emails   boolean  optional    

true will return contacts opted out from email communications. false will show contacts that have not opted out. Do not pass the filter to show all. Example: true

opted_out_sms   boolean  optional    

true will return contacts opted out from SMS communications. false will show contacts that have not opted out. Do not pass the filter to show all. Example: true

tags   string[]  optional    

Filter contacts by assigned tag(s). Array.

Get a Contact

requires authentication

Returns the information about a specific Contact

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/21225/contacts/10000023" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/contacts/10000023"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/contacts/10000023';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "lead-carter.raleigh@kreiger.info",
        "team_id": "3089",
        "profile_url": "https://loopspark.com/go/client/3089/lead-carter.raleigh@kreiger.info",
        "email": "carter.raleigh@kreiger.info",
        "active": false,
        "first_name": "Callie",
        "last_name": "Towne",
        "mobile_phone": "13576039800",
        "can_contact_by_email": true,
        "can_contact_by_sms": true,
        "unsubscribed_email_at": null,
        "unsubscribed_sms_at": null,
        "is_prospect": true,
        "created_at": "2026-10-03T08:00:36+00:00",
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "client": null
    }
}
 

Request      

GET api/franchise/v1/{siteId}/contacts/{contactId}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID. Example: 21225

contactId   string     

The contact ID. Example: 10000023

Update a Contact

requires authentication

Updates the information of a Contact

Example request:
curl --request PUT \
    "https://loopspark.com/api/franchise/v1/21224/contacts/10000024" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"subscribed_email\": false,
    \"subscribed_sms\": false,
    \"period_count\": 27,
    \"club_count\": 5,
    \"referrer\": {
        \"first_name\": \"John\",
        \"last_name\": \"Doe\",
        \"email\": \"john.doe@example.com\",
        \"phone\": \"555-123-4567\",
        \"location\": \"New York\"
    }
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21224/contacts/10000024"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "subscribed_email": false,
    "subscribed_sms": false,
    "period_count": 27,
    "club_count": 5,
    "referrer": {
        "first_name": "John",
        "last_name": "Doe",
        "email": "john.doe@example.com",
        "phone": "555-123-4567",
        "location": "New York"
    }
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21224/contacts/10000024';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'subscribed_email' => false,
            'subscribed_sms' => false,
            'period_count' => 27,
            'club_count' => 5.0,
            'referrer' => ['first_name' => 'John', 'last_name' => 'Doe', 'email' => 'john.doe@example.com', 'phone' => '555-123-4567', 'location' => 'New York'],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "lead-mhamill@marvin.net",
        "team_id": "8863",
        "profile_url": "https://loopspark.com/go/client/8863/lead-mhamill@marvin.net",
        "email": "mhamill@marvin.net",
        "active": false,
        "first_name": "Rhett",
        "last_name": "Kuhn",
        "mobile_phone": "13573900260",
        "can_contact_by_email": true,
        "can_contact_by_sms": true,
        "unsubscribed_email_at": null,
        "unsubscribed_sms_at": null,
        "is_prospect": true,
        "created_at": "2026-10-03T08:00:36+00:00",
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "client": null
    }
}
 

Example response (422, Trying to SMS opt in a contact that manually opted out):


{
    "error": true,
    "message": "The contact cannot be opted-in to SMS because they sent the opt-out text"
}
 

Request      

PUT api/franchise/v1/{siteId}/contacts/{contactId}

PATCH api/franchise/v1/{siteId}/contacts/{contactId}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21224

contactId   string     

The contact ID Example: 10000024

Body Parameters

subscribed_email   boolean  optional    

Unsubscribes an entity from email communications if false. Subscribes if true. Example: false

subscribed_sms   boolean  optional    

Unsubscribes an entity from SMS communications if false. Subscribes if true. Example: false

period_count   number  optional    

Manually set the period count for this entity. Must be at least 0. Example: 27

club_count   number  optional    

Manually set the club count for this entity (requires manual_club_count to be enabled for the studio). Must be at least 0. Example: 5

referrer   object  optional    

Object containing referrer information with optional properties: first_name, last_name, email, phone, location.

first_name   string  optional    

Example: John

last_name   string  optional    

Example: Doe

email   string  optional    

Must be a valid email address. Example: john.doe@example.com

phone   string  optional    

Example: 555-123-4567

location   string  optional    

Example: New York

Tags

Add a Tag

requires authentication

Assigns a Tag to a specific Contact

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/contacts/10000023/tags" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"tag\": \"Member\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/contacts/10000023/tags"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag": "Member"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/contacts/10000023/tags';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'tag' => 'Member',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "646980841",
        "team_id": "4071",
        "profile_url": "https://loopspark.com/go/client/4071/646980841",
        "email": "emanuel.emmerich@yahoo.com",
        "active": true,
        "first_name": "Kirsten",
        "last_name": "Ward",
        "mobile_phone": "13577071833",
        "can_contact_by_email": true,
        "can_contact_by_sms": true,
        "unsubscribed_email_at": null,
        "unsubscribed_sms_at": null,
        "is_prospect": false,
        "created_at": null,
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "client": {
            "id": "646980841",
            "team_id": "4071",
            "profile_url": "https://loopspark.com/go/client/4071/646980841",
            "email": "emanuel.emmerich@yahoo.com",
            "active": true,
            "first_name": "Kirsten",
            "last_name": "Ward",
            "mobile_phone": "13577071833",
            "ltv": 0,
            "phone_home": null,
            "phone_work": null,
            "referred_by": null,
            "address": {
                "city": "Sibylmouth",
                "address": "79388 Cristobal Ways\nCruickshankview, GA 41591-5182",
                "postal_code": "60787-7092",
                "state": "CA"
            },
            "emergency_contact": {
                "email": "colt.balistreri@hotmail.com",
                "name": "Mrs. Abbey Gaylord MD",
                "phone": "13573113802",
                "relationship": "wife"
            },
            "birthday": "1990-05-05T12:02:12+00:00",
            "creation_date": "2025-03-26T01:00:14+00:00",
            "refreshed_at": null,
            "tags": [
                {
                    "name": "Member",
                    "assigned_at": "2025-01-15T09:30:00+00:00"
                }
            ],
            "custom_fields": [],
            "can_contact_by_email": true,
            "can_contact_by_sms": true,
            "unsubscribed_email_at": null,
            "unsubscribed_sms_at": null,
            "unsubscribed_sms_text": false,
            "referrer": {
                "first_name": null,
                "last_name": null,
                "email": null,
                "phone": null,
                "location": null
            },
            "stats": {
                "period_count": null,
                "club_count": 0,
                "open_discussions": 0,
                "open_todos": 0
            },
            "social": {
                "twitter": [],
                "linkedin": [],
                "facebook": [],
                "instagram": []
            }
        }
    }
}
 

Request      

POST api/franchise/v1/{siteId}/contacts/{contactId}/tags

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

contactId   string     

The contact ID Example: 10000023

Body Parameters

tag   string     

The tag. Must be at least 1 character. Example: Member

Remove a Tag

requires authentication

Removes a Tag from a specific Contact. If the Contact does not have the Tag assigned, it will return an error 400

Example request:
curl --request DELETE \
    "https://loopspark.com/api/franchise/v1/21225/contacts/10000023/tags" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"tag\": \"signed-up-flow\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/contacts/10000023/tags"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag": "signed-up-flow"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/contacts/10000023/tags';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'tag' => 'signed-up-flow',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": "1187404954",
        "team_id": "4071",
        "profile_url": "https://loopspark.com/go/client/4071/1187404954",
        "email": "emanuel.emmerich@yahoo.com",
        "active": true,
        "first_name": "Kirsten",
        "last_name": "Ward",
        "mobile_phone": "13577071833",
        "can_contact_by_email": true,
        "can_contact_by_sms": true,
        "unsubscribed_email_at": null,
        "unsubscribed_sms_at": null,
        "is_prospect": false,
        "created_at": null,
        "tags": [
            {
                "name": "Member",
                "assigned_at": "2025-01-15T09:30:00+00:00"
            }
        ],
        "custom_fields": [],
        "client": {
            "id": "1187404954",
            "team_id": "4071",
            "profile_url": "https://loopspark.com/go/client/4071/1187404954",
            "email": "emanuel.emmerich@yahoo.com",
            "active": true,
            "first_name": "Kirsten",
            "last_name": "Ward",
            "mobile_phone": "13577071833",
            "ltv": 0,
            "phone_home": null,
            "phone_work": null,
            "referred_by": null,
            "address": {
                "city": "Sibylmouth",
                "address": "79388 Cristobal Ways\nCruickshankview, GA 41591-5182",
                "postal_code": "60787-7092",
                "state": "CA"
            },
            "emergency_contact": {
                "email": "colt.balistreri@hotmail.com",
                "name": "Mrs. Abbey Gaylord MD",
                "phone": "13573113802",
                "relationship": "wife"
            },
            "birthday": "1990-05-05T12:02:12+00:00",
            "creation_date": "2025-03-26T01:00:14+00:00",
            "refreshed_at": null,
            "tags": [
                {
                    "name": "Member",
                    "assigned_at": "2025-01-15T09:30:00+00:00"
                }
            ],
            "custom_fields": [],
            "can_contact_by_email": true,
            "can_contact_by_sms": true,
            "unsubscribed_email_at": null,
            "unsubscribed_sms_at": null,
            "unsubscribed_sms_text": false,
            "referrer": {
                "first_name": null,
                "last_name": null,
                "email": null,
                "phone": null,
                "location": null
            },
            "stats": {
                "period_count": null,
                "club_count": 0,
                "open_discussions": 0,
                "open_todos": 0
            },
            "social": {
                "twitter": [],
                "linkedin": [],
                "facebook": [],
                "instagram": []
            }
        }
    }
}
 

Example response (400):


{
    "error": true,
    "message": "The contact does not have the tag signed-up"
}
 

Request      

DELETE api/franchise/v1/{siteId}/contacts/{contactId}/tags

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

contactId   string     

The contact ID Example: 10000023

Body Parameters

tag   string     

The tag. Must be at least 1 character. Example: signed-up-flow

Custom Fields

Get Contact Custom Fields

requires authentication

Retrieve all custom field values for a specific contact. Returns field definition along with current value.

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/100001/contacts/100000001/custom-fields" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/100001/contacts/100000001/custom-fields"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/100001/contacts/100000001/custom-fields';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "EMERGENCY_CONTACT",
            "value": "Jane Doe - 555-1234"
        }
    ]
}
 

Request      

GET api/franchise/v1/{siteId}/contacts/{contactId}/custom-fields

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio's site identifier (e.g., MindBody site ID). Example: 100001

contactId   string     

The contact's external identifier (e.g., MindBody client ID). Example: 100000001

Update Contact Custom Fields

requires authentication

Batch update custom field values for a specific contact. Can update multiple fields in a single request.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/100001/contacts/100000001/custom-fields" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"fields\": [
        {
            \"id\": \"EMERGENCY_CONTACT\",
            \"value\": \"John Doe - 555-1234\"
        },
        {
            \"id\": \"THREE_CS\",
            \"value\": \"challenge\"
        }
    ]
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/100001/contacts/100000001/custom-fields"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "fields": [
        {
            "id": "EMERGENCY_CONTACT",
            "value": "John Doe - 555-1234"
        },
        {
            "id": "THREE_CS",
            "value": "challenge"
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/100001/contacts/100000001/custom-fields';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'fields' => [
                ['id' => 'EMERGENCY_CONTACT', 'value' => 'John Doe - 555-1234'],
                ['id' => 'THREE_CS', 'value' => 'challenge'],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "EMERGENCY_CONTACT",
            "value": "Jane Doe - 555-5678"
        }
    ]
}
 

Request      

POST api/franchise/v1/{siteId}/contacts/{contactId}/custom-fields

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio's site identifier (e.g., MindBody site ID). Example: 100001

contactId   string     

The contact's external identifier (e.g., MindBody client ID). Example: 100000001

Body Parameters

fields   object[]     

Array of field updates. Must have at least 1 items.

*   object  optional    
id   string     

The custom field ID (e.g., "EMERGENCY_CONTACT") Example: architecto

value   string  optional    

nullable The field value Example: architecto

id   string     

The custom field ID/slug (e.g., "EMERGENCY_CONTACT", "THREE_CS"). Must match an existing stored value. Example: EMERGENCY_CONTACT

value   string  optional    

The value to set. Use null to clear the field. Type depends on field definition (string, boolean, date, etc.). Example: John Doe - 555-1234

Notes

Create a Note

requires authentication

Adds a note to a Contact (a Client or a Prospect), found by its contact id, dated now. The note is plain text: HTML is shown as text, and line breaks are kept. To categorize it, send the id of one of the studio's active note categories (see "List Note Categories"). Each call creates a new note, so do not retry a request that already succeeded. The note has no staff author; LoopSpark shows its author as "Integration".

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/contacts/10000023/notes" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"note\": \"Called about the 6-week challenge.\",
    \"note_category_id\": \"9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/contacts/10000023/notes"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "note": "Called about the 6-week challenge.",
    "note_category_id": "9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/contacts/10000023/notes';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'note' => 'Called about the 6-week challenge.',
            'note_category_id' => '9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "data": {
        "id": "3f0d6a52-8a4e-4c43-9b1e-6f3c2a1d7e90",
        "note": "<p>Called about the 6-week challenge.</p>",
        "note_category_id": "9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10",
        "created_at": "2026-09-28T14:05:00+00:00"
    }
}
 

Example response (422):


{
    "message": "The note category is not available for this studio.",
    "errors": {
        "note_category_id": [
            "The note category is not available for this studio."
        ]
    }
}
 

Request      

POST api/franchise/v1/{siteId}/contacts/{contactId}/notes

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID. Example: 21225

contactId   string     

The contact ID (a client or a prospect). Example: 10000023

Body Parameters

note   string     

The note text, at least 2 characters. Plain text; line breaks are kept. Example: Called about the 6-week challenge.

note_category_id   string  optional    

Optional. The id of one of the studio's active note categories. Example: 9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10

Messages

Send a Text Message

requires authentication

Sends one SMS to a Contact (a Client or a Prospect), found by its contact id, from the studio's texting number, in the same conversation thread staff use in LoopSpark. Placeholders are filled in as in LoopSpark: write the name in double curly braces, for example FIRST_NAME. The message can be at most 3 SMS segments long, counted before placeholders are filled in. Opt-outs (STOP) and the usual opt-out notice work exactly as for a staff send, and the text counts against the studio's SMS bundle. A 201 means the text was queued; delivery happens asynchronously. Each call sends a new text, so do not retry a request that already succeeded. The text has no staff sender; the LoopSpark web app shows its sender as "Integration". Returns 422 when the person is muted, has opted out of texts, or has no valid mobile number, or when the text is empty once placeholders are filled in. Returns 403 when the studio is suspended.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/contacts/10000023/messages" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"message\": \"Hi there, see you at the 6pm class!\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/contacts/10000023/messages"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "message": "Hi there, see you at the 6pm class!"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/contacts/10000023/messages';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'message' => 'Hi there, see you at the 6pm class!',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "data": {
        "id": "3f0d6a52-8a4e-4c43-9b1e-6f3c2a1d7e90",
        "discussion_id": "9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10",
        "created_at": "2026-10-02T14:05:00+00:00"
    }
}
 

Example response (422):


{
    "message": "This client cannot be contacted by text message",
    "errors": {
        "message": [
            "This client cannot be contacted by text message"
        ]
    }
}
 

Request      

POST api/franchise/v1/{siteId}/contacts/{contactId}/messages

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID. Example: 21225

contactId   string     

The contact ID (a client or a prospect). Example: 10000023

Body Parameters

message   string     

The text to send, at most 3 SMS segments before placeholders are filled in. Placeholders (a name such as FIRST_NAME in double curly braces) are filled in. Example: Hi there, see you at the 6pm class!

Custom Fields

Franchise API endpoints for managing custom field definitions and client values.

Get Custom Fields

requires authentication

Retrieve all custom field definitions for this franchise.

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/custom-fields" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/custom-fields"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/custom-fields';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "EMERGENCY_CONTACT",
            "name": "Emergency Contact",
            "type": "string",
            "field_options": null,
            "description": "Person to contact in case of emergency",
            "visible_in_ui": true,
            "editable_by_staff": true,
            "sort_order": 0
        },
        {
            "id": "MEMBERSHIP_TYPE",
            "name": "Membership Type",
            "type": "single_select",
            "field_options": [
                {
                    "value": "basic",
                    "label": "Basic",
                    "is_active": true,
                    "sort_order": 1
                },
                {
                    "value": "premium",
                    "label": "Premium",
                    "is_active": true,
                    "sort_order": 2
                }
            ],
            "description": "Choose membership level",
            "visible_in_ui": true,
            "editable_by_staff": false,
            "sort_order": 1
        }
    ]
}
 

Request      

GET api/franchise/v1/custom-fields

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Deferred Tags

All the Endpoints regarding the deferred tags of a specific studio. This feature allows you to tell LoopSpark that when we see a client or prospect with the specified UniqueID, LoopSpark should automatically add a Tag and attach it them. Prospects' UniqueID can be retrieved from the "Prospect list" endpoint, using the returned id of the prospect

Add deferred tag

requires authentication

Add one or multiple deferred tag rules for a specific Studio. LoopSpark will automatically add the chosen tag when the rule is applied.

Example request:
curl --request POST \
    "https://loopspark.com/api/franchise/v1/21225/tags_deferred" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"clients\": [
        {
            \"id\": \"1000017\",
            \"tag\": \"true\"
        }
    ]
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/tags_deferred"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "clients": [
        {
            "id": "1000017",
            "tag": "true"
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/tags_deferred';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'clients' => [
                ['id' => '1000017', 'tag' => 'true'],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "client_id": "776",
            "tag": "eius",
            "valid_since": "2026-10-03T08:00:36+00:00"
        },
        {
            "client_id": "575",
            "tag": "corporis",
            "valid_since": "2026-10-03T08:00:36+00:00"
        }
    ]
}
 

Request      

POST api/franchise/v1/{siteId}/tags_deferred

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

Body Parameters

clients   object[]  optional    
id   string     

The MBO UniqueID of the client, or the ID of a Prospect. Example: 1000017

tag   string     

The name of the tag to eventually add to this client. The Tag is not required to exist at the moment, it will be created the first time thi rule is applied. Must be at least 3 characters. Example: true

Remove Deferred Tag rule

requires authentication

Removes a defered tag rule for a specific Client

Example request:
curl --request DELETE \
    "https://loopspark.com/api/franchise/v1/21225/tags_deferred/10000012" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}" \
    --header "Content-Type: application/json" \
    --data "{
    \"tag\": \"beginner\"
}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/tags_deferred/10000012"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "tag": "beginner"
};

fetch(url, {
    method: "DELETE",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/tags_deferred/10000012';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
            'Content-Type' => 'application/json',
        ],
        'json' => [
            'tag' => 'beginner',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "error": false,
    "deleted": true,
    "success": true
}
 

Example response (400):


{
    "error": true,
    "message": "No rule was found with that unique id and type combination"
}
 

Request      

DELETE api/franchise/v1/{siteId}/tags_deferred/{uniqueid}

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

Content-Type        

Example: application/json

URL Parameters

siteId   string     

The studio ID Example: 21225

uniqueid   string     

The MBO UniqueID of the Client, or the ID of a Prospect Example: 10000012

Body Parameters

tag   string     

The name of the tag of the deferred tag rule. Example: beginner

Note Categories

The studio's note categories, used to file a note created on a Client, Prospect or Contact.

List Note Categories

requires authentication

The studio's active note categories. Send a category's id as note_category_id when creating a note.

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/21225/note-categories" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/21225/note-categories"
);

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/21225/note-categories';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "9b2f0c1e-4c1a-4a53-9a5e-2f7f3d1c8b10",
            "name": "Objection Note"
        }
    ]
}
 

Request      

GET api/franchise/v1/{siteId}/note-categories

Headers

Authorization        

Example: Bearer {YOUR_FRANCHISE_TOKEN}

URL Parameters

siteId   string     

The studio ID Example: 21225

Search

This endpoint allows franchise owners to search and list contacts (clients or prospects) across all studios within their franchise. It provides filtering by email, mobile phone, tags, and specific studio site IDs, as well as sorting and pagination capabilities. This is useful for managing contacts across multiple locations and finding duplicates or shared contacts between studios.

requires authentication

Search for a Contact across the whole franchise.

Example request:
curl --request GET \
    --get "https://loopspark.com/api/franchise/v1/search?per_page=10&page=1&sort_by=id&sort_direction=asc&email=&mobile_phone=&tags[]=signed-up&site_ids[]=" \
    --header "Authorization: Bearer {YOUR_FRANCHISE_TOKEN}"
const url = new URL(
    "https://loopspark.com/api/franchise/v1/search"
);

const params = {
    "per_page": "10",
    "page": "1",
    "sort_by": "id",
    "sort_direction": "asc",
    "email": "",
    "mobile_phone": "",
    "tags[0]": "signed-up",
    "site_ids[0]": "",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_FRANCHISE_TOKEN}",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://loopspark.com/api/franchise/v1/search';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_FRANCHISE_TOKEN}',
        ],
        'query' => [
            'per_page' => '10',
            'page' => '1',
            'sort_by' => 'id',
            'sort_direction' => 'asc',
            'email' => '',
            'mobile_phone' => '',
            'tags[0]' => 'signed-up',
            'site_ids[0]' => '',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": "822569775",
            "team_id": "6789",
            "profile_url": "https://loopspark.com/go/client/6789/822569775",
            "email": "kconsidine@hotmail.com",
            "active": true,
            "first_name": "Pauline",
            "last_name": "Schultz",
            "mobile_phone": "13577773167",
            "can_contact_by_email": true,
            "can_contact_by_sms": true,
            "unsubscribed_email_at": null,
            "unsubscribed_sms_at": null,
            "is_prospect": false,
            "created_at": null,
            "tags": [
                {
                    "name": "Member",
                    "assigned_at": "2025-01-15T09:30:00+00:00"
                }
            ],
            "custom_fields": [],
            "client": {
                "id": "822569775",
                "team_id": "6789",
                "profile_url": "https://loopspark.com/go/client/6789/822569775",
                "email": "kconsidine@hotmail.com",
                "active": true,
                "first_name": "Pauline",
                "last_name": "Schultz",
                "mobile_phone": "13577773167",
                "ltv": 0,
                "phone_home": null,
                "phone_work": null,
                "referred_by": null,
                "address": {
                    "city": "North Eileen",
                    "address": "4706 Madisen Avenue Suite 636\nNew Josianetown, WV 49408-0159",
                    "postal_code": "79150-6985",
                    "state": "CA"
                },
                "emergency_contact": {
                    "email": "mitchell.matilda@yahoo.com",
                    "name": "Mina Bauch",
                    "phone": "13573073143",
                    "relationship": "wife"
                },
                "birthday": "1972-08-25T23:37:54+00:00",
                "creation_date": "2023-10-10T09:48:20+00:00",
                "refreshed_at": null,
                "tags": [
                    {
                        "name": "Member",
                        "assigned_at": "2025-01-15T09:30:00+00:00"
                    }
                ],
                "custom_fields": [],
                "can_contact_by_email": true,
                "can_contact_by_sms": true,
                "unsubscribed_email_at": null,
                "unsubscribed_sms_at": null,
                "unsubscribed_sms_text": false,
                "referrer": {
                    "first_name": null,
                    "last_name": null,
                    "email": null,
                    "phone": null,
                    "location": null
                },
                "stats": {
                    "period_count": 12,
                    "club_count": 0,
                    "open_discussions": 0,
                    "open_todos": 0
                },
                "social": {
                    "twitter": [],
                    "linkedin": [],
                    "facebook": [],
                    "instagram": []
                }
            }
        },
        {
            "id": "26665356",
            "team_id": "9537",
            "profile_url": "https://loopspark.com/go/client/9537/26665356",
            "email": "schmitt.eleanore@nienow.com",
            "active": true,
            "first_name": "Kenna",
            "last_name": "Ortiz",
            "mobile_phone": "13579799206",
            "can_contact_by_email": true,
            "can_contact_by_sms": true,
            "unsubscribed_email_at": null,
            "unsubscribed_sms_at": null,
            "is_prospect": false,
            "created_at": null,
            "tags": [
                {
                    "name": "Member",
                    "assigned_at": "2025-01-15T09:30:00+00:00"
                }
            ],
            "custom_fields": [],
            "client": {
                "id": "26665356",
                "team_id": "9537",
                "profile_url": "https://loopspark.com/go/client/9537/26665356",
                "email": "schmitt.eleanore@nienow.com",
                "active": true,
                "first_name": "Kenna",
                "last_name": "Ortiz",
                "mobile_phone": "13579799206",
                "ltv": 0,
                "phone_home": null,
                "phone_work": null,
                "referred_by": null,
                "address": {
                    "city": "New Greyson",
                    "address": "6747 Wolf Light\nNew Brookeburgh, TX 37725",
                    "postal_code": "56023-5576",
                    "state": "CA"
                },
                "emergency_contact": {
                    "email": "ritchie.dorris@klocko.com",
                    "name": "Trinity Swaniawski",
                    "phone": "13572063041",
                    "relationship": "wife"
                },
                "birthday": "1987-05-12T05:35:52+00:00",
                "creation_date": "2024-05-14T05:57:03+00:00",
                "refreshed_at": null,
                "tags": [
                    {
                        "name": "Member",
                        "assigned_at": "2025-01-15T09:30:00+00:00"
                    }
                ],
                "custom_fields": [],
                "can_contact_by_email": true,
                "can_contact_by_sms": true,
                "unsubscribed_email_at": null,
                "unsubscribed_sms_at": null,
                "unsubscribed_sms_text": false,
                "referrer": {
                    "first_name": null,
                    "last_name": null,
                    "email": null,
                    "phone": null,
                    "location": null
                },
                "stats": {
                    "period_count": 12,
                    "club_count": 0,
                    "open_discussions": 0,
                    "open_todos": 0
                },
                "social": {
                    "twitter": [],
                    "linkedin": [],
                    "facebook": [],
                    "instagram": []
                }
            }
        }
    ]
}