Skip to main content

Common workflows

Each sequence below is the shortest correct path. Endpoint details are in the API reference.

Check whether a property is free​

curl -X POST 'https://core.mtro.app/bookings/public/check-availability' \
-H 'Authorization: Bearer mtro_your_api_key' \
-H 'Content-Type: application/json' \
-d '{"propertyId":"<propertyId>","startDate":1790000000000,"endDate":1792592000000}'

Dates are millisecond timestamps. endDate is checkout and is exclusive: a stay may start on the same calendar day another one ends.

Availability accounts for confirmed bookings, explicit date blocks, and synced external calendars such as Airbnb. Prospective bookings do not block.

Take in a new enquiry​

  1. Read the pipeline stages. Stage ids are per-organization, so never hardcode them.

    curl 'https://core.mtro.app/pipeline-stages/get/entity/<entityId>' \
    -H 'Authorization: Bearer mtro_your_api_key'
  2. Create the lead. tenantName is the only required field. Note the field names — they are not name/email/phone.

    curl -X POST 'https://core.mtro.app/webhooks/create-lead' \
    -H 'Authorization: Bearer mtro_your_api_key' \
    -H 'Content-Type: application/json' \
    -d '{
    "tenantName": "Jordan Ellis",
    "emailAddress": "[email protected]",
    "phoneNumber": "+14695550123",
    "service": "Manual",
    "destination": "Dallas, TX",
    "numberOfTravelers": 4,
    "stageId": "<stageId>"
    }'
  3. Move it along the funnel as it progresses:

    curl -X POST 'https://core.mtro.app/webhooks/update-stage' \
    -H 'Authorization: Bearer mtro_your_api_key' \
    -H 'Content-Type: application/json' \
    -d '{"leadId":"<leadId>","stageId":"<nextStageId>"}'

Scopes: leads.read to list stages and leads, leads.write to create and move.

Turn a lead into a booking​

POST /bookings/create needs propertyId, startDate, and endDate. The primary guest is identified by email, not by guest id: Core matches an existing guest on that address or creates one from the name fields.

curl -X POST 'https://core.mtro.app/bookings/create' \
-H 'Authorization: Bearer mtro_your_api_key' \
-H 'Content-Type: application/json' \
-d '{
"propertyId": "<propertyId>",
"startDate": 1790000000000,
"endDate": 1792592000000,
"guestEmail": "[email protected]",
"guestName": "Jordan",
"guestSurname": "Ellis",
"rate": 120,
"status": "pending",
"leadId": "<leadId>"
}'

Pass leadId so the booking stays linked to the enquiry it came from.

Message a guest​

  1. Find the conversation — POST /conversations/get lists them, and GET /conversations/get/entity/:entityId returns the thread for a booking when you pass entityType=booking.

  2. Send:

    curl -X POST 'https://core.mtro.app/conversations/send-message' \
    -H 'Authorization: Bearer mtro_your_api_key' \
    -H 'Content-Type: application/json' \
    -d '{"conversationId":"<conversationId>","message":"Your keys are in the lockbox."}'

This sends a real message over SMS, email, or in-app depending on the conversation. There is no dry run. Test on https://dev.core.mtro.app.

Scopes: conversations.read and conversations.write.

Schedule maintenance​

  1. Read the vendors: GET /vendors/get/all.

  2. Create the task:

    curl -X POST 'https://core.mtro.app/tasks/create' \
    -H 'Authorization: Bearer mtro_your_api_key' \
    -H 'Content-Type: application/json' \
    -d '{
    "propertyId": "<propertyId>",
    "vendorId": "<vendorId>",
    "date": 1790000000000,
    "notes": "Turnover clean between stays",
    "status": "pending"
    }'

Scopes: vendors.read and tasks.write.

Notice what changed​

There are no outbound webhooks, so polling is the only option. A workable shape:

ResourceEndpointSuggested interval
ConversationsPOST /conversations/getevery 2–5 minutes
LeadsGET /webhooks/get/allevery 5 minutes
BookingsGET /bookings/get/allevery 15 minutes
Properties, vendorsGET /properties/get/all, GET /vendors/get/allhourly or on demand

POST /conversations/get accepts page and limit in the body and reports total, so poll the first page and only walk further when the count moves. The others return the full set on every call, so cache what you read and compare against it rather than re-reading in a loop.