Operator & team
Endpoints for your operator account and the people who work in it: read and
update the operator record, manage members, define roles with a permission grid,
and create portal users. These are client-role,
operator-scoped endpoints; each is gated by a
specific permission (for example operators.settings:write,
users.role:write, users.user:write).
Endpoints
Section titled “Endpoints”| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/my-operator |
Read the operator record | operator |
| PUT | /api/my-operator |
Update the operator record | operator |
| GET | /api/client/dashboard |
Portal bootstrap aggregate | operator |
| GET | /api/me/permissions |
Your effective permissions + catalogue | session |
| GET | /api/my-operator/members |
List members | operator |
| GET | /api/my-operator/users-search |
Search users to add as members | operator |
| POST | /api/my-operator/members |
Add a member | operator |
| PUT | /api/my-operator/members/:id |
Change a member’s role | operator |
| DELETE | /api/my-operator/members/:id |
Remove a member | operator |
| GET | /api/my-roles |
List roles | operator |
| GET | /api/my-roles/:id |
Read a role + its permission grid | operator |
| POST | /api/my-roles |
Create a role | operator |
| PUT | /api/my-roles/:id |
Update a role, permissions and venues | operator |
| DELETE | /api/my-roles/:id |
Delete a role | operator |
| GET | /api/my-users |
List portal users | operator |
| POST | /api/my-users |
Create a portal user | operator |
| PUT | /api/my-users/:id |
Update a user | operator |
| DELETE | /api/my-users/:id |
Delete a user | operator |
The operator record
Section titled “The operator record”GET /api/my-operator reads your company record; PUT updates it (name is
required).
curl -X PUT "https://thesidedoor.co/api/my-operator" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Acme Hospitality","company_name":"Acme Ltd","company_reg":"12345678","vat_number":"GB123456789"}'const res = await fetch("https://thesidedoor.co/api/my-operator", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Acme Hospitality", company_name: "Acme Ltd", company_reg: "12345678", vat_number: "GB123456789", }),});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-operator");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "name" => "Acme Hospitality", "company_name" => "Acme Ltd", "company_reg" => "12345678", "vat_number" => "GB123456789", ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true }Dashboard bootstrap
Section titled “Dashboard bootstrap”GET /api/client/dashboard returns the aggregate the client portal loads on
sign-in: the operator, your permissions, payout status, headline stats and your
venues.
curl "https://thesidedoor.co/api/client/dashboard" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch("https://thesidedoor.co/api/client/dashboard", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` },});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/client/dashboard");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "operator": { "id": "op_1", "name": "Acme Hospitality", "status": "active", "stripe_connect_status": "enabled" }, "payout_status": { "provider": "stripe", "status": "enabled", "source": "cached" }, "stats": { "venues": 3, "total_guests": 4200, "month_guests": 380, "revenue": 0 }, "staff_summary": { "count": 6 }, "venues": [{ "id": "ven_123", "name": "The Attic", "status": "live" }]}Your permissions
Section titled “Your permissions”GET /api/me/permissions returns your effective permission map plus the
permission catalogue (tree) used to build a role grid. It supports
If-None-Match/ETag for a 304.
curl "https://thesidedoor.co/api/me/permissions" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch("https://thesidedoor.co/api/me/permissions", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}` },});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/me/permissions");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"],]);$data = json_decode(curl_exec($ch), true);{ "role": "client", "portal": "client", "permissions": { "bookings": { "read": true, "write": true } }, "tree": [ { "key": "bookings", "label": "Bookings", "actions": ["read", "write"] }, { "key": "users", "label": "Users & roles", "actions": [], "children": [ { "key": "users.role", "label": "Roles", "actions": ["read", "write"] } ] } ]}Members
Section titled “Members”Members link existing client users to your operator account.
GET /api/my-operator/users-search?q= finds candidate users, then
POST /api/my-operator/members adds one (role defaults to member).
curl -X POST "https://thesidedoor.co/api/my-operator/members" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"user_id":"usr_abc","role":"manager"}'const res = await fetch("https://thesidedoor.co/api/my-operator/members", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "usr_abc", role: "manager" }),});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-operator/members");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["user_id" => "usr_abc", "role" => "manager"]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "id": "om_9f2a" }Change a member’s role with PUT /api/my-operator/members/:id ({"role":"..."})
or remove them with DELETE /api/my-operator/members/:id; both return
{ "success": true }. The operator’s primary contact cannot be removed.
A role is created with a name and description, then its permission grid and venue
scope are set with PUT. Create a role first:
curl -X POST "https://thesidedoor.co/api/my-roles" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Front of House","description":"FOH staff"}'const res = await fetch("https://thesidedoor.co/api/my-roles", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Front of House", description: "FOH staff" }),});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-roles");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "name" => "Front of House", "description" => "FOH staff", ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "id": "role_7f3" }Set the permission grid
Section titled “Set the permission grid”PUT /api/my-roles/:id replaces the permission grid and venue scope. Each grid
row is { permission_key, can_read, can_write } - pull the available keys from
the tree in /api/me/permissions. An empty or omitted
venue_ids means all account venues.
curl -X PUT "https://thesidedoor.co/api/my-roles/ROLE_ID" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Front of House", "permissions": [ { "permission_key": "bookings", "can_read": true, "can_write": true }, { "permission_key": "guests.table", "can_read": true, "can_write": false } ], "venue_ids": ["VENUE_ID"] }'const res = await fetch("https://thesidedoor.co/api/my-roles/ROLE_ID", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Front of House", permissions: [ { permission_key: "bookings", can_read: true, can_write: true }, { permission_key: "guests.table", can_read: true, can_write: false }, ], venue_ids: ["VENUE_ID"], }),});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-roles/ROLE_ID");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "name" => "Front of House", "permissions" => [ ["permission_key" => "bookings", "can_read" => true, "can_write" => true], ["permission_key" => "guests.table", "can_read" => true, "can_write" => false], ], "venue_ids" => ["VENUE_ID"], ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true }GET /api/my-roles/:id returns the stored grid as permissions and the scoped
venue_ids; DELETE /api/my-roles/:id removes a role (system roles cannot be
deleted).
Portal users sign in and are assigned exactly one role. POST /api/my-users
requires name, email, password and a role_id that belongs to your
account.
curl -X POST "https://thesidedoor.co/api/my-users" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Jane Doe","email":"jane@example.com","password":"s3cret-pass","role_id":"ROLE_ID","phone":"+44..."}'const res = await fetch("https://thesidedoor.co/api/my-users", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Jane Doe", email: "jane@example.com", password: "s3cret-pass", role_id: "ROLE_ID", phone: "+44...", }),});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-users");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "name" => "Jane Doe", "email" => "jane@example.com", "password" => "s3cret-pass", "role_id" => "ROLE_ID", "phone" => "+44...", ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "id": "usr_new1" }PUT /api/my-users/:id accepts any subset of name, email, password,
phone and role_id (a new password is only set when non-empty);
DELETE /api/my-users/:id removes a user (you cannot delete yourself). Both
return { "success": true }.