Skip to content

Guests

The guests CRM endpoints expose your operator guest roster: search across everyone who has booked with your venues, drill into a single guest’s history, billing and social network, apply tags, and create lightweight walk-in guests. All guests-CRM routes require the operator role and are scoped to your own account and venues.

Method Path Purpose Auth
GET /api/my-guests Guest roster (paginated) operator
GET /api/my-guests/search Search the roster operator
GET /api/my-guests/network Full operator guest graph operator
GET /api/my-guests/:id One guest with bookings, stats, tags operator
GET /api/my-guests/:id/billing Guest billing ledger operator
GET /api/my-guests/:id/activity Recent activity (last 10) operator
GET /api/my-guests/:id/bookings All of a guest’s bookings operator
GET /api/my-guests/:id/network 1-hop network around a guest operator
PUT /api/my-guests/:id/tags Replace a guest’s tags at a scope operator
GET /api/my-venues/:venueId/guests/search Venue-scoped guest search operator
POST /api/my-venues/:venueId/guests/walk-in Create or match a walk-in guest operator
GET /api/users/:id/score A guest’s Sidedoor score and breakdown operator
GET /api/my-guests/:id/ask-aimy Whether Aimy is available for this guest operator
GET /api/my-guests/:id/brief Compact facts: visits, no-shows, spend, favourite venue operator
GET /api/my-guests/:id/loyalty-summary Compact loyalty badges: score, trust and a recommended action operator
POST /api/my-guests/merge Merge duplicate guest accounts into one operator

Roster search matches name, email or phone (minimum 2 characters, capped at 50 results). Use /api/my-guests with pagination to page the whole roster.

Terminal window
curl "https://thesidedoor.co/api/my-guests/search?q=sam" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"data": [
{
"id": "usr_abc",
"name": "Sam Lee",
"email": "sam@example.com",
"phone": "+44...",
"membership_tier": "black",
"loyalty_points": 240,
"successful_visits": 12,
"venue_ids": "ven_123,ven_456"
}
]
}

GET /api/my-guests/:id returns the guest together with their recent bookings, a marketing engagement summary, a products breakdown (reservations, events, guest list, vouchers), any reviews they have left at your venues, aggregate stats, and their tags. Tags come in four sets: your manual global and per_venue tags, read-only auto loyalty tags, and read-only rule_auto tags applied by a tag rule.

Terminal window
curl "https://thesidedoor.co/api/my-guests/usr_abc" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"guest": {
"id": "usr_abc",
"name": "Sam Lee",
"email": "sam@example.com",
"phone": "+44...",
"membership_tier": "black",
"dob": "1990-04-12"
},
"bookings": [
{ "id": "bkg_9a1", "status": "checked_in", "guest_count": 4, "booking_date": "2026-06-20", "venue_name": "The Attic" }
],
"marketing": {
"email_sent": 12,
"email_opened": 9,
"email_clicked": 3,
"email_open_rate": 75,
"email_click_rate": 25,
"push_enabled": true,
"favourite_category": "Cocktail bars"
},
"products": {
"reservations": { "total": 15, "attended": 12, "cancelled": 1, "no_show": 0, "avg_guests": 3.2 },
"events": { "booked": 4, "attended": 3, "last_at": "2026-05-02T19:00:00Z" },
"guestlist": { "total": 2, "accepted": 2, "declined": 0 },
"vouchers": { "purchased": 3, "redeemed": 2, "outstanding": 1, "gifted": 1 }
},
"reviews": {
"count": 1,
"avg_rating": 5,
"items": [
{ "rating": 5, "comment": "Great night", "source_type": "post_visit", "venue_id": "ven_123", "venue_name": "The Attic", "created_at": "2026-06-21T10:00:00Z" }
]
},
"stats": {
"loyalty_points": 240,
"loyalty_points_network": 980,
"loyalty_tier": "Gold",
"loyalty_progress": { "tier": "Gold", "next_tier": "Platinum", "points": 980, "pts_to_next": 220, "pct": 78 },
"confirmed_visits": 12,
"door_rejections": 0,
"no_shows": 0,
"total_bookings": 15,
"total_spend_pence": 48200,
"spend_breakdown": { "events_pence": 12000, "vouchers_pence": 6000, "deposits_pence": 30200 },
"keys_spent": 30
},
"is_connection": false,
"tags": {
"global": ["vip"],
"per_venue": [{ "venue_id": "ven_123", "venue_name": "The Attic", "tags": ["regular"] }],
"auto": [{ "key": "big_spender", "label": "Big spender" }],
"rule_auto": [{ "venue_id": "ven_123", "venue_name": "The Attic", "tag_label": "weeknight-regular" }]
}
}

The related reads - GET /api/my-guests/:id/billing, /activity, /bookings and /network - follow the same auth and path shape:

Terminal window
curl "https://thesidedoor.co/api/my-guests/usr_abc/billing" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"data": [
{ "id": "led_1", "type": "spend", "delta": -5000, "paid_delta": -5000, "free_delta": 0, "note": "Table deposit", "booking_id": "bkg_9a1", "created_at": "2026-06-20T20:00:00Z" }
]
}

Each row splits its delta into paid_delta (keys the guest paid for) and free_delta (promotional keys). type is spend or refund.

GET /api/my-guests/:id/brief returns a compact set of facts about a guest at your venues: visits, no-shows, cancellations, total spend, their favourite venue, and their first and last visit. It’s the same data shown on the guest tile’s summary and in the Aimy chat panel - see Aimy, your portal assistant.

Terminal window
curl "https://thesidedoor.co/api/my-guests/usr_abc/brief" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"guest_id": "usr_abc",
"brief": {
"name": "Sam Lee",
"visits": 12,
"no_shows": 0,
"cancellations": 1,
"total_spend_pence": 48200,
"favourite_venue": "The Attic",
"first_visit": "2025-02-14",
"last_visit": "2026-06-20"
},
"has_data": true
}

has_data is false when the guest has no bookings yet at your venues, so brief is a mostly-empty shell.

GET /api/my-guests/:id/loyalty-summary returns a compact, render-ready set of loyalty badges for a guest tile: their Sidedoor score, a trust verdict, a recommended action, their automatic loyalty tags, and whether their birthday is coming up.

Terminal window
curl "https://thesidedoor.co/api/my-guests/usr_abc/loyalty-summary" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"score": 72,
"trust": "trusted",
"action": "none",
"auto_tags": [{ "key": "big_spender", "label": "Big spender" }],
"birthday_near": false
}

A walk-in guest (never registered, with no reachable contact) has no meaningful score or tags, and the endpoint returns just { "walk_in": true } instead.

POST /api/my-guests/merge collapses a group of duplicate guest accounts - for example one created by a booking and another imported separately - onto a single survivor. Pass the primary_id to keep and the full ids array (2 to 50, the primary included); every other account’s bookings, reservations, vouchers, reviews and roster membership at your venues move onto the primary, and the duplicates drop off your roster. This only affects your own operator’s view of the accounts - it never merges or deletes the underlying guest accounts themselves, which may also belong to other operators.

Terminal window
curl -X POST "https://thesidedoor.co/api/my-guests/merge" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"primary_id":"usr_abc","ids":["usr_abc","usr_def"]}'
{ "success": true, "primary_id": "usr_abc", "merged": 1 }

Every id must already be on your roster, and all accounts you merge must be guests of your own operator - you can’t merge in an account from outside your roster.

Replace a guest’s tags at a scope. scope is global (default) or venue; when venue, pass the venue_id. Tags are sent as an array and fully replace the existing set at that scope.

Terminal window
curl -X PUT "https://thesidedoor.co/api/my-guests/usr_abc/tags" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"scope":"venue","venue_id":"VENUE_ID","tags":["regular","big-spender"]}'
{ "success": true }

GET /api/my-guests/:id/network returns a one-hop graph centred on a guest: the centre node plus their direct connections. Each node is { id, name, degree, tier }, and the centre node also carries centre: true. The response wraps nodes, edges and centre_id.

Terminal window
curl "https://thesidedoor.co/api/my-guests/usr_abc/network" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"nodes": [
{ "id": "usr_abc", "name": "Sam Lee", "degree": 3, "tier": "black", "centre": true },
{ "id": "usr_def", "name": "Alex Ray", "degree": 5, "tier": "gold" }
],
"edges": [{ "source": "usr_abc", "target": "usr_def" }],
"centre_id": "usr_abc"
}

GET /api/my-guests/network returns the whole operator guest graph. Its nodes are richer, carrying email, phone, account_type, company_name, sidedoor_score, loyalty_tags and client_tags alongside id, name, tier and degree. Connected guests who are not your own (they have never booked at your venues) appear as redacted nodes: { id, name: null, anonymous: true, degree }, with no contact details or score. This graph returns nodes and edges only (no centre_id).

GET /api/my-venues/:venueId/guests/search is a compact search (min 2 chars, 12 results) for picking a guest when creating a booking at a specific venue.

Terminal window
curl "https://thesidedoor.co/api/my-venues/VENUE_ID/guests/search?q=sam" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{
"data": [
{
"id": "usr_abc",
"name": "Sam Lee",
"email": "sam@example.com",
"phone": "+44...",
"profile_image_url": null,
"sidedoor_score": 72,
"sidedoor_loyalty_tags": [{ "key": "big_spender", "label": "Big spender" }],
"client_tags": ["regular"]
}
]
}

Each row carries the guest’s sidedoor_score, read-only sidedoor_loyalty_tags and your own manual client_tags (global plus this venue), matching the columns in the Guests pane.

Create a lightweight guest for someone booking on the night. Supply at least one of name, email or phone. If you omit name, the guest is created as “Walk-in”; a missing email is synthesised. Use the returned id as the user_id when creating a staff booking.

If the email or phone you send matches a guest already in the system, the endpoint returns that existing account instead of creating a new one, with existing: true in the response. A newly created guest omits that field.

Terminal window
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/guests/walk-in" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Jordan Fox","phone":"+44..."}'
{ "id": "usr_walkin1", "name": "Jordan Fox", "email": "walkin-1a2b3c4d@local", "phone": "+44..." }

A matched existing account instead returns existing: true:

{ "id": "usr_abc", "name": "Sam Lee", "email": "sam@example.com", "phone": "+44...", "existing": true }

GET /api/users/:id/score returns a guest’s Sidedoor score and its breakdown. As an operator you can read the score of any guest linked to one of your venues (by a booking or reservation). The score is not part of the guest-detail response, it lives on this dedicated endpoint.

Terminal window
curl "https://thesidedoor.co/api/users/usr_abc/score" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"

total is the 0-100 headline score and rank its label. parts breaks the raw total into its four contributors, and loyaltyTags are the automatic loyalty tags derived from the same signals.

{
"total": 72,
"raw": 148,
"rank": "Regular",
"parts": { "loyalty": 60, "connections": 40, "age": 35, "tier": 13 },
"loyaltyTags": [{ "key": "big_spender", "label": "Big spender" }],
"inputs": { "connectionCount": 8, "ageDays": 255, "tier": "black" },
"previous": { "score": 68, "at": "2026-09-11T00:00:00Z" }
}

previous is the stored baseline from about a week ago (or null if none is recorded yet), so you can show a trend against the current total.

GET /api/my-guests/:id/ask-aimy reports whether the Aimy add-on is enabled on your account, so a client can show or hide an “Ask Aimy about this guest” entry point for the guest. Asking the actual question happens in the Aimy chat panel inside the operator portal, not over the API - see Aimy, your portal assistant.

Terminal window
curl "https://thesidedoor.co/api/my-guests/usr_abc/ask-aimy" \
-H "Authorization: Bearer $SIDEDOOR_TOKEN"
{ "entitled": true }

Without the add-on, this returns { "entitled": false, "addon": "aimy" } instead of a 403, so you can render a locked state without treating it as an error.