๐ก AI Bot Integration
Answer guest messages with your AI, using live reservation context from LIKE MAGIC
Important: This documentation describes the integration with our test environment. Test your integration thoroughly there before moving to production. Contact api-support@likemagic.tech to get onboarded and receive your test environment credentials.
The Idea
Guests write to the hotel on WhatsApp, SMS or Booking.com. LIKE MAGIC receives every message, matches it to the guest and their reservation and hands it to your AI together with that context. Your AI answers with text, an image or a document, and LIKE MAGIC delivers the answer to the guest on the same channel. When a request needs a person, your AI hands the conversation to the hotel team, and the team can hand it back.
How It Works
The Flow:
- Guest writes to the hotel on WhatsApp, SMS or Booking.com
- LIKE MAGIC receives the message through Bird and matches it to a guest identity and a reservation where possible
- LIKE MAGIC posts the message to your webhook with the guest and reservation context
- Your AI fetches more context from the LIKE MAGIC APIs if it needs it, for example booked services or open tasks
- Your AI posts its answer to the LIKE MAGIC callback: text, an image or a document
- LIKE MAGIC delivers the answer to the guest on the channel the guest used
- Your AI or the hotel team hands over whenever a person should take the conversation
sequenceDiagram
participant Bird
participant "LIKE MAGIC"
participant "AI partner"
Bird->>"LIKE MAGIC": 1: Incoming guest message
activate "LIKE MAGIC"
"LIKE MAGIC"->>"LIKE MAGIC": 2: Find guest identity & reservation
"LIKE MAGIC"->>"AI partner": 3: Guest message with guest & reservation context
activate "AI partner"
"AI partner"->>"AI partner": 4: Create answer
opt Fetch additional information
"AI partner"->>"LIKE MAGIC": 5: API request
"LIKE MAGIC"-->>"AI partner": response
end
"AI partner"-->>"LIKE MAGIC": 6: Answer via callback (text, image or document)
deactivate "AI partner"
"LIKE MAGIC"->>Bird: 7: Deliver answer to guest
deactivate "LIKE MAGIC"
Matching a message to a guest identity does not always succeed, and the identity of a conversation can change later. Read Guest identity and conversation history before you design how you store guest state.
What You Get
- Guest messages from WhatsApp, SMS and Booking.com in one webhook, with reservation and property context
- Text, image and document answers delivered on the guest's own channel
- Images and files sent by the guest, available through an authenticated download
- Handover to the hotel team and back, with the full conversation history kept in LIKE MAGIC
- Access to reservations, services, tasks and conversation history through the LIKE MAGIC APIs
Perfect For
- AI agent vendors building guest messaging for hotels
- Conversational AI platforms that want reservation aware answers
- Hotel groups connecting their own AI to LIKE MAGIC messaging
Technical Details
Before You Start
You need four things before your first test message:
| What | Where it comes from |
|---|---|
| Test environment credentials and tenant | api-support@likemagic.tech |
lm-webhook-secret value for both directions |
Your technical contact at LIKE MAGIC |
| A service account for the LIKE MAGIC APIs | Created by an Account Admin, see Manage Service Accounts |
| Your webhook URL | You, shared with your technical contact at LIKE MAGIC |
Request only the scopes your AI needs. A typical AI agent uses:
| Scope | Used for |
|---|---|
reservation.read, property.read, unit.read, service.read |
Reservation and property context |
guest_conversation.write |
Resolving conversations and changing their property |
guest_message_attachment.read |
Downloading images and files sent by the guest |
magic_id.read |
Guest links |
order_item.write |
Selling upsells |
task.read, task.write |
Raising and tracking service requests |
webhook_subscription.write |
Subscribing to business events |
Test Environment
The URLs below use your tenant specific Base URL (see Prerequisites & Configuration). They show as a pattern on the central docs and are filled in automatically on your own environment.
Open this page on your own environment
We strongly suggest heading over to your tenant's own copy of this page now, to see your real URLs instead of the patterns below and eliminate any guessing:
https://monitoring.<your-domain>/api-doc/use-case/partner/ai-bot-integration/
Just swap in your <your-domain>.
| Item | Value |
|---|---|
| Callback URL | /api/integration-service/webhook/unified-messaging/<tenant>/callback |
| Base URL | |
| Tenant | likemagic-test |
| WhatsApp number | +1 (226) 640-5907 (test only, production uses the hotel's own number) |
Receiving Guest Messages
LIKE MAGIC posts (POST) every event for your AI to the webhook URL you provided.
Security: every request carries the lm-webhook-secret header. Reject requests whose header does not match the secret you received.
| Property | Type | Contract | Description |
|---|---|---|---|
| event | Enum | required | lm.message, lm.image, lm.file, lm.notification or lm.action |
| type | Enum | required | Channel of the message (whatsapp, sms, booking_dot_com) or the action (HUMAN_TAKE_OVER, AI_TAKE_OVER) |
| tenantName | String | required | Name of the LIKE MAGIC customer |
| conversationId | UUID | required | Identifier of the conversation. Send it back on every callback |
| messageId | UUID | optional | Set for messages. Identifier of the specific message |
| pmsReservationId | String | optional | Linked reservation id from the PMS, if known |
| pmsPropertyId | String | optional | Linked property id from the PMS. Not sent when no property can be determined |
| content | String or Object | optional | Set for messages. A string for lm.message, an Image, File or Notification object for the other events |
| createdAt | DateTime String (ISO-8601) |
required | When the message was created |
| guestActor | Guest Actor | optional | Guest details. Sent for lm.message, lm.image, lm.file and lm.notification, not for lm.action |
Guest Actor
| Property | Type | Contract | Description |
|---|---|---|---|
| guestIdentityId | Long | optional | Durable identifier of the guest across conversations and channels within one tenant. Empty while the contact is not yet identified, see Guest identity and conversation history |
| userProfileId | Long | optional | Identifier of the guest profile. Use it to load conversation history and profile data. Set whenever guestIdentityId is set and a profile exists |
| firstName | String | optional | First name of the guest |
| lastName | String | optional | Last name of the guest |
| phone | String | optional | Phone number of the guest |
| String | optional | Email of the guest |
Image
| Property | Type | Contract | Description |
|---|---|---|---|
| mediaUrl | String | required | URL of the image, download it as described in Downloading guest media |
| caption | String | optional | Caption written by the guest |
File
| Property | Type | Contract | Description |
|---|---|---|---|
| mediaUrl | String | required | URL of the file, download it as described in Downloading guest media |
| contentType | String | required | Type of the file, for example video/mp4, audio/ogg or application/pdf |
| caption | String | optional | Caption written by the guest |
Notification
lm.notification carries the trigger based template messages LIKE MAGIC sent to the guest, so your AI knows what the guest already received.
| Property | Type | Contract | Description |
|---|---|---|---|
| text | String | required | Text of the template message |
| templateName | Enum | required | COMPANION_ADDED, PRE_CHECKIN_REMINDER, RESERVATION_CREATED, CHECKIN, CHECKED_IN, CHECKIN_INTRODUCTION, CHECKIN_REMINDER, CHECKOUT_ALERT, CHECKOUT_FOLLOW_UP, CHECKOUT_INTRODUCTION, ID_CHECK_DECLINED, INVOICE or RESERVATION_CANCELED |
| language | Enum | required | Language of the template, for example DE or EN |
Example: Guest Message
{
"event": "lm.message",
"type": "whatsapp",
"tenantName": "LIKE MAGIC",
"conversationId": "CONVERSATION_ID_LIKEMAGIC",
"messageId": "MESSAGE_ID_LIKEMAGIC",
"pmsReservationId": "PMS_RESERVATION_ID",
"pmsPropertyId": "PMS_PROPERTY_ID",
"content": "Where can I find the ironing board?",
"createdAt": "2025-01-14T15:55:59.547Z",
"guestActor": {
"guestIdentityId": 12345,
"userProfileId": 67890,
"firstName": "John",
"lastName": "Smith",
"email": "john.smith@example.com",
"phone": "+15550100000"
}
}
Example: Guest Image
{
"event": "lm.image",
"type": "whatsapp",
"tenantName": "LIKE MAGIC",
"conversationId": "CONVERSATION_ID_LIKEMAGIC",
"messageId": "MESSAGE_ID_LIKEMAGIC",
"pmsReservationId": "PMS_RESERVATION_ID",
"pmsPropertyId": "PMS_PROPERTY_ID",
"content": {
"mediaUrl": "https://media.likemagic.tech/VgYYE0oT",
"caption": "Is this the right coffee machine?"
},
"createdAt": "2025-01-14T15:55:59.547Z",
"guestActor": {
"guestIdentityId": 12345,
"userProfileId": 67890,
"firstName": "John",
"lastName": "Smith",
"email": "john.smith@example.com",
"phone": "+15550100000"
}
}
Example: Guest File
{
"event": "lm.file",
"type": "whatsapp",
"tenantName": "LIKE MAGIC",
"conversationId": "CONVERSATION_ID_LIKEMAGIC",
"messageId": "MESSAGE_ID_LIKEMAGIC",
"pmsReservationId": "PMS_RESERVATION_ID",
"pmsPropertyId": "PMS_PROPERTY_ID",
"content": {
"mediaUrl": "https://media.likemagic.tech/VgYYE0oT",
"contentType": "application/pdf",
"caption": "My train ticket"
},
"createdAt": "2025-01-14T15:55:59.547Z",
"guestActor": {
"guestIdentityId": 12345,
"userProfileId": 67890,
"firstName": "John",
"lastName": "Smith",
"email": "john.smith@example.com",
"phone": "+15550100000"
}
}
Example: Notification
{
"event": "lm.notification",
"type": "whatsapp",
"tenantName": "LIKE MAGIC",
"conversationId": "CONVERSATION_ID_LIKEMAGIC",
"messageId": "MESSAGE_ID_LIKEMAGIC",
"pmsReservationId": "PMS_RESERVATION_ID",
"pmsPropertyId": "PMS_PROPERTY_ID",
"content": {
"text": "Hey John, your room is almost ready. Complete your online check-in now.",
"templateName": "CHECKIN_REMINDER",
"language": "EN"
},
"createdAt": "2025-08-19T03:36:11.344Z",
"guestActor": {
"guestIdentityId": 12345,
"userProfileId": 67890,
"firstName": "John",
"lastName": "Smith",
"email": "john.smith@example.com",
"phone": "+15550100000"
}
}
Example: Handover Signals
The hotel team took over the conversation (HUMAN_TAKE_OVER) or handed it back to your AI (AI_TAKE_OVER):
{
"event": "lm.action",
"type": "HUMAN_TAKE_OVER",
"tenantName": "LIKE MAGIC",
"pmsPropertyId": "PMS_PROPERTY_ID",
"conversationId": "CONVERSATION_ID_LIKEMAGIC"
}
Stop answering a conversation after HUMAN_TAKE_OVER and resume after AI_TAKE_OVER.
Downloading Guest Media
The mediaUrl of a guest image or file is not public. Download it with your service account token and the guest_message_attachment.read scope:
GET /api/unified-messaging/guest-conversations/attachments?mediaUrl={mediaUrl}
URL encode the mediaUrl value.
Guest Identity and Conversation History
Four identifiers appear in these events and they serve different purposes.
| Identifier | Use it for | Stability |
|---|---|---|
guestIdentityId |
Grouping a guest's conversations over time | Durable, but can be superseded |
userProfileId |
Loading conversation history and profile data through our APIs | Stable in normal operation. Can change when two profiles are merged |
conversationId |
Correlating events within one conversation | Not durable, can change |
messageId |
Matching a specific message across changes | Stable, preserved in all cases |
When guestIdentityId Is Empty
We set guestIdentityId once we have matched the sender's phone number or email to a known guest. It is empty when:
- the guest writes from a phone number or email we have never linked to a reservation
- the contact belongs to an OTA placeholder address rather than to the guest
- our person record matching could not resolve the contact unambiguously
While it is empty we cannot tell you which other conversations belong to the same person, and neither history endpoint helps. Use guestActor.phone if you need a best effort grouping on your side.
The value fills in on its own once a reservation carrying that contact reaches us. It is not guaranteed to ever fill in. A contact that never appears on a reservation stays unidentified.
Loading a Guest's Conversation History
With the userProfileId from the event:
| Endpoint | Returns |
|---|---|
GET /guest-conversations/by-user-profile-id/{userProfileId} |
Paginated conversations of that guest, newest first |
GET /guest-conversations/{conversationId}/messages |
Paginated messages of one conversation |
If you only hold a guestIdentityId, resolve the profile first with GET /user-profiles/by-guest-identity-id/{guestIdentityId}. The id in the response is the userProfileId. A guest identity almost always has a profile, but the call can return 404, so handle that case.
Do not swap the two ids
Do not pass a guestIdentityId where a userProfileId is expected. They are separate id spaces. The call usually returns 404 and can in rare cases return a different guest's conversations.
When a Conversation Is Re-keyed
When a contact that was not identified later becomes identified, or an operator resolves an identity conflict, we move the affected messages onto the identified guest and the conversationId changes.
Plan for the following:
- A
conversationIdyou hold can change. The next event for that guest carries the new one. - A conversation can be re-keyed more than once.
- We delete a conversation once a move leaves it empty, so a
GETon thatconversationIdreturns 404. Treat that as superseded rather than invalid and resolve the guest again. guestIdentityIdcan also be superseded. When an operator merges several identities we create a new one, so two values you have seen can both be replaced by a third.- A conversation can hold messages for more than one contact address of the same guest, for example a phone number and an email. A move covers the messages of the contact that was linked, so a conversation can be split.
Because of the last point, do not assume that every message you received under the old conversationId belongs to the newly resolved guest. Match on messageId, which we preserve. Messages you saw under the old conversation reappear under the new one with the same message ids.
We do not currently push an event when a conversation is re-keyed. You can derive it from the events you already receive. If the guestIdentityId for a conversationId you know changes, or a conversation you know appears under a new id, re-read that guest's history through the endpoints above.
Answering the Guest
Post (POST) your answer to the callback URL from the Test Environment table. Send the lm-webhook-secret header you received with every callback.
| Property | Type | Contract | Description |
|---|---|---|---|
| event | Enum | required | ai.response |
| conversationId | UUID | required | The conversationId of the guest message you answer |
| messageId | String | required | Your own unique identifier of this answer, for example 42827c8e-0354-4b13-9319-5093b83886fe |
| type | String | optional | Kept for compatibility, send text |
| content | String | optional | Text of the answer. Required when there is no attachment |
| attachment | Attachment | optional | One image or document. Required when there is no content |
Send at least one of content and attachment. To send several files, post one callback per file, each with its own messageId.
Attachment
| Property | Type | Contract | Description |
|---|---|---|---|
| mediaUrl | String | required | Public https URL of the file. LIKE MAGIC passes it on to the messaging channel, which downloads it when the message is sent |
| contentType | String | required | image/jpeg or image/png for images. application/pdf, Word (.doc, .docx), Excel (.xls, .xlsx), PowerPoint (.ppt, .pptx) or text/plain for documents |
| fileName | String | optional | Name the guest sees on a document, for example Microwave manual.pdf. Taken from the last part of the URL path when missing. Not shown for images |
Hosting your files:
- Keep the
mediaUrlreachable for at least 24 hours. LIKE MAGIC retries failed deliveries and the hotel team views the file in LIKE MAGIC from the same URL. - Prefer permanent URLs for knowledge base media. A signed URL that expires before delivery means the guest receives nothing.
- Use a new URL when you change a file. WhatsApp caches a URL for 10 minutes.
- Keep images under 5 MB and documents under 20 MB.
How the Answer Reaches the Guest
| Channel | Text only | Attachment only | Text and attachment |
|---|---|---|---|
| Text message | Image or document without caption | Text up to 1024 characters becomes the caption of the image or document. Longer text is sent first as its own message, followed by the image or document | |
| Booking.com | Email reply | Not delivered, send text with it | Email reply with the file attached |
| SMS | Text message | Not delivered | Not delivered, send text only |
Responses
| Status | Meaning |
|---|---|
200 |
Answer accepted. Delivery to the guest happens asynchronously |
400 |
Answer rejected. The body names the problem: neither content nor attachment, a mediaUrl that is not an absolute https URL or a contentType that is not allowed |
Channel rules from the table above are applied after the 200. An attachment that the channel cannot deliver is dropped without a callback to you, so check the channel (type of the guest message) before you send one.
Example: Text Answer
curl -X 'POST' \
'{{monitoringUrl}}/api/integration-service/webhook/unified-messaging/<tenant>/callback' \
-H 'Content-Type: application/json' \
-H 'lm-webhook-secret: <your secret>' \
-d '{
"event": "ai.response",
"conversationId": "367fecba-bc23-418b-aef2-7943180860d4",
"messageId": "42827c8e-0354-4b13-9319-5093b83886fe",
"type": "text",
"content": "The ironing board is in the wardrobe, on the left side."
}'
Example: Image With Caption
curl -X 'POST' \
'{{monitoringUrl}}/api/integration-service/webhook/unified-messaging/<tenant>/callback' \
-H 'Content-Type: application/json' \
-H 'lm-webhook-secret: <your secret>' \
-d '{
"event": "ai.response",
"conversationId": "367fecba-bc23-418b-aef2-7943180860d4",
"messageId": "9b1d3f2a-6c4e-4f7a-8d21-0e5b7c3a9f10",
"type": "text",
"content": "The ironing board is in the wardrobe, on the left side.",
"attachment": {
"mediaUrl": "https://kb.example.com/rooms/ironing-board.jpg",
"contentType": "image/jpeg"
}
}'
Example: Document Without Text
curl -X 'POST' \
'{{monitoringUrl}}/api/integration-service/webhook/unified-messaging/<tenant>/callback' \
-H 'Content-Type: application/json' \
-H 'lm-webhook-secret: <your secret>' \
-d '{
"event": "ai.response",
"conversationId": "367fecba-bc23-418b-aef2-7943180860d4",
"messageId": "c5e8a1d4-2b7f-4e93-a6c0-71d4f2b8e356",
"type": "text",
"attachment": {
"mediaUrl": "https://kb.example.com/manuals/microwave.pdf",
"contentType": "application/pdf",
"fileName": "Microwave manual.pdf"
}
}'
Handing Over to a Human
Tell LIKE MAGIC that a person needs to take over. The conversation moves to the hotel team in LIKE MAGIC with its full history.
curl -X 'POST' \
'{{monitoringUrl}}/api/integration-service/webhook/unified-messaging/<tenant>/callback' \
-H 'Content-Type: application/json' \
-H 'lm-webhook-secret: <your secret>' \
-d '{
"event": "ai.action",
"conversationId": "367fecba-bc23-418b-aef2-7943180860d4",
"type": "HUMAN_INTERVENTION_NEEDED"
}'
Managing Conversations
Through the Unified Messaging API your AI can resolve a conversation and change its associated pmsPropertyId. Both endpoints return the current guest conversation.
Creating Tasks
Integrate with the Task API to turn guest requests into tasks for the hotel team and track their progress. See Manage Tasks.
Key APIs
- Unified Messaging API: conversations, message history and guest media
- Integration API: the callback for answers and handover
- Task Management API: service requests for the hotel team
Integration Certification
Before moving to production, your integration is certified by LIKE MAGIC.
Checklist:
- Develop and test the integration in the test environment
- Implement all events and endpoints you use, including handover
- Handle errors and edge cases, such as re-keyed conversations and
400responses - Test every message type in both directions, including images and documents
Schedule the certification call:
- Email api-support@likemagic.tech with the subject "Integration Certification Request"
- Include your test tenant name, a short summary of your integration and the use cases you support
- Share your availability for a call
- We review your integration and schedule the meeting
During the call we verify the integration and give feedback before approving it for production.
New to the platform? Start with Authentication and Manage Service Accounts.